Closed bartecargo closed 3 years ago
I was just thinking that I hadn't seen a new request in a while... I agree. This would be a nice feature to add.
I haven't seen the IL that C# generates for the record classes. It's going to be either very easy or very hard. I'll try to find some upcoming time to work on it. Meanwhile, if someone can run ILDASM on a small record class and post it, that would be really helpful.
The following code:
record SimpleType(int Value);
Generates (in the Rider IDE):
.class private auto ansi beforefieldinit
SimpleType
extends [System.Runtime]System.Object
implements class [System.Runtime]System.IEquatable`1<class SimpleType>
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 01 00 00 ) // .....
// unsigned int8(1) // 0x01
.custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor([in] unsigned int8)
= (01 00 00 00 00 ) // .....
// unsigned int8(0) // 0x00
.interfaceimpl type class [System.Runtime]System.IEquatable`1<class SimpleType>
.custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor([in] unsigned int8)
= (01 00 00 00 00 ) // .....
// unsigned int8(0) // 0x00
.field private initonly int32 '<Value>k__BackingField'
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.custom instance void [System.Runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggerBrowsableState)
= (01 00 00 00 00 00 00 00 ) // ........
// int32(0) // 0x00000000
.method public hidebysig specialname rtspecialname instance void
.ctor(
int32 Value
) cil managed
{
.maxstack 8
// [1 19 - 1 28]
IL_0000: ldarg.0 // this
IL_0001: ldarg.1 // Value
IL_0002: stfld int32 SimpleType::'<Value>k__BackingField'
// [1 1 - 1 30]
IL_0007: ldarg.0 // this
IL_0008: call instance void [System.Runtime]System.Object::.ctor()
IL_000d: nop
IL_000e: ret
} // end of method SimpleType::.ctor
.method family hidebysig virtual newslot specialname instance class [System.Runtime]System.Type
get_EqualityContract() cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
IL_0000: ldtoken SimpleType
IL_0005: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_000a: ret
} // end of method SimpleType::get_EqualityContract
.method public hidebysig specialname instance int32
get_Value() cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [1 19 - 1 28]
IL_0000: ldarg.0 // this
IL_0001: ldfld int32 SimpleType::'<Value>k__BackingField'
IL_0006: ret
} // end of method SimpleType::get_Value
.method public hidebysig specialname instance void modreq ([System.Runtime]System.Runtime.CompilerServices.IsExternalInit)
set_Value(
int32 'value'
) cil managed
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [1 19 - 1 28]
IL_0000: ldarg.0 // this
IL_0001: ldarg.1 // 'value'
IL_0002: stfld int32 SimpleType::'<Value>k__BackingField'
IL_0007: ret
} // end of method SimpleType::set_Value
.method public hidebysig virtual instance string
ToString() cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 00 00 00 ) // .....
// unsigned int8(0) // 0x00
.maxstack 2
.locals init (
[0] class [System.Runtime]System.Text.StringBuilder V_0
)
IL_0000: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor()
IL_0005: stloc.0 // V_0
IL_0006: ldloc.0 // V_0
IL_0007: ldstr "SimpleType"
IL_000c: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0011: pop
IL_0012: ldloc.0 // V_0
IL_0013: ldstr " { "
IL_0018: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_001d: pop
IL_001e: ldarg.0 // this
IL_001f: ldloc.0 // V_0
IL_0020: callvirt instance bool SimpleType::PrintMembers(class [System.Runtime]System.Text.StringBuilder)
IL_0025: brfalse.s IL_0033
IL_0027: ldloc.0 // V_0
IL_0028: ldstr " "
IL_002d: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0032: pop
IL_0033: ldloc.0 // V_0
IL_0034: ldstr "}"
IL_0039: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_003e: pop
IL_003f: ldloc.0 // V_0
IL_0040: callvirt instance string [System.Runtime]System.Object::ToString()
IL_0045: ret
} // end of method SimpleType::ToString
.method family hidebysig virtual newslot instance bool
PrintMembers(
class [System.Runtime]System.Text.StringBuilder builder
) cil managed
{
.maxstack 2
.locals init (
[0] int32 V_0
)
IL_0000: ldarg.1 // builder
IL_0001: ldstr "Value"
IL_0006: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_000b: pop
IL_000c: ldarg.1 // builder
IL_000d: ldstr " = "
IL_0012: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0017: pop
IL_0018: ldarg.1 // builder
IL_0019: ldarg.0 // this
IL_001a: call instance int32 SimpleType::get_Value()
IL_001f: stloc.0 // V_0
IL_0020: ldloca.s V_0
IL_0022: constrained. [System.Runtime]System.Int32
IL_0028: callvirt instance string [System.Runtime]System.Object::ToString()
IL_002d: callvirt instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(string)
IL_0032: pop
IL_0033: ldc.i4.1
IL_0034: ret
} // end of method SimpleType::PrintMembers
.method public hidebysig static specialname bool
op_Inequality(
class SimpleType r1,
class SimpleType r2
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.maxstack 8
IL_0000: ldarg.0 // r1
IL_0001: ldarg.1 // r2
IL_0002: call bool SimpleType::op_Equality(class SimpleType, class SimpleType)
IL_0007: ldc.i4.0
IL_0008: ceq
IL_000a: ret
} // end of method SimpleType::op_Inequality
.method public hidebysig static specialname bool
op_Equality(
class SimpleType r1,
class SimpleType r2
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.maxstack 8
IL_0000: ldarg.0 // r1
IL_0001: ldarg.1 // r2
IL_0002: beq.s IL_0013
IL_0004: ldarg.0 // r1
IL_0005: brfalse.s IL_0010
IL_0007: ldarg.0 // r1
IL_0008: ldarg.1 // r2
IL_0009: callvirt instance bool SimpleType::Equals(class SimpleType)
IL_000e: br.s IL_0011
IL_0010: ldc.i4.0
IL_0011: br.s IL_0014
IL_0013: ldc.i4.1
IL_0014: ret
} // end of method SimpleType::op_Equality
.method public hidebysig virtual instance int32
GetHashCode() cil managed
{
.maxstack 8
IL_0000: call class [System.Collections]System.Collections.Generic.EqualityComparer`1<!0/*class [System.Runtime]System.Type*/> class [System.Collections]System.Collections.Generic.EqualityComparer`1<class [System.Runtime]System.Type>::get_Default()
IL_0005: ldarg.0 // this
IL_0006: callvirt instance class [System.Runtime]System.Type SimpleType::get_EqualityContract()
IL_000b: callvirt instance int32 class [System.Collections]System.Collections.Generic.EqualityComparer`1<class [System.Runtime]System.Type>::GetHashCode(!0/*class [System.Runtime]System.Type*/)
IL_0010: ldc.i4 -1521134295 // 0xa5555529
IL_0015: mul
IL_0016: call class [System.Collections]System.Collections.Generic.EqualityComparer`1<!0/*int32*/> class [System.Collections]System.Collections.Generic.EqualityComparer`1<int32>::get_Default()
IL_001b: ldarg.0 // this
IL_001c: ldfld int32 SimpleType::'<Value>k__BackingField'
IL_0021: callvirt instance int32 class [System.Collections]System.Collections.Generic.EqualityComparer`1<int32>::GetHashCode(!0/*int32*/)
IL_0026: add
IL_0027: ret
} // end of method SimpleType::GetHashCode
.method public hidebysig virtual instance bool
Equals(
object obj
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.maxstack 8
IL_0000: ldarg.0 // this
IL_0001: ldarg.1 // obj
IL_0002: isinst SimpleType
IL_0007: callvirt instance bool SimpleType::Equals(class SimpleType)
IL_000c: ret
} // end of method SimpleType::Equals
.method public hidebysig virtual newslot instance bool
Equals(
class SimpleType other
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.maxstack 8
IL_0000: ldarg.1 // other
IL_0001: brfalse.s IL_002e
IL_0003: ldarg.0 // this
IL_0004: callvirt instance class [System.Runtime]System.Type SimpleType::get_EqualityContract()
IL_0009: ldarg.1 // other
IL_000a: callvirt instance class [System.Runtime]System.Type SimpleType::get_EqualityContract()
IL_000f: call bool [System.Runtime]System.Type::op_Equality(class [System.Runtime]System.Type, class [System.Runtime]System.Type)
IL_0014: brfalse.s IL_002e
IL_0016: call class [System.Collections]System.Collections.Generic.EqualityComparer`1<!0/*int32*/> class [System.Collections]System.Collections.Generic.EqualityComparer`1<int32>::get_Default()
IL_001b: ldarg.0 // this
IL_001c: ldfld int32 SimpleType::'<Value>k__BackingField'
IL_0021: ldarg.1 // other
IL_0022: ldfld int32 SimpleType::'<Value>k__BackingField'
IL_0027: callvirt instance bool class [System.Collections]System.Collections.Generic.EqualityComparer`1<int32>::Equals(!0/*int32*/, !0/*int32*/)
IL_002c: br.s IL_002f
IL_002e: ldc.i4.0
IL_002f: ret
} // end of method SimpleType::Equals
.method public hidebysig virtual newslot instance class SimpleType
'<Clone>$'() cil managed
{
.maxstack 8
IL_0000: ldarg.0 // this
IL_0001: newobj instance void SimpleType::.ctor(class SimpleType)
IL_0006: ret
} // end of method SimpleType::'<Clone>$'
.method family hidebysig specialname rtspecialname instance void
.ctor(
class SimpleType original
) cil managed
{
.maxstack 8
IL_0000: ldarg.0 // this
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: nop
IL_0007: ldarg.0 // this
IL_0008: ldarg.1 // original
IL_0009: ldfld int32 SimpleType::'<Value>k__BackingField'
IL_000e: stfld int32 SimpleType::'<Value>k__BackingField'
IL_0013: ret
} // end of method SimpleType::.ctor
.method public hidebysig instance void
Deconstruct(
[out] int32& Value
) cil managed
{
.maxstack 8
IL_0000: ldarg.1 // Value
IL_0001: ldarg.0 // this
IL_0002: call instance int32 SimpleType::get_Value()
IL_0007: stind.i4
IL_0008: ret
} // end of method SimpleType::Deconstruct
.property instance class [System.Runtime]System.Type EqualityContract()
{
.get instance class [System.Runtime]System.Type SimpleType::get_EqualityContract()
} // end of property SimpleType::EqualityContract
.property instance int32 Value()
{
.get instance int32 SimpleType::get_Value()
.set instance void modreq ([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) SimpleType::set_Value(int32)
} // end of property SimpleType::Value
} // end of class SimpleType
A simplified overview of the above IL code generated by ILSpy from the assembly:
// SimpleType
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
internal class SimpleType : IEquatable<SimpleType>
{
protected virtual Type EqualityContract
{
[CompilerGenerated]
get
{
return typeof(SimpleType);
}
}
public int Value
{
get;
set/*init*/;
}
public SimpleType(int Value)
{
this.Value = Value;
base..ctor();
}
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("SimpleType");
stringBuilder.Append(" { ");
if (PrintMembers(stringBuilder))
{
stringBuilder.Append(" ");
}
stringBuilder.Append("}");
return stringBuilder.ToString();
}
protected virtual bool PrintMembers(StringBuilder builder)
{
builder.Append("Value");
builder.Append(" = ");
builder.Append(Value.ToString());
return true;
}
[System.Runtime.CompilerServices.NullableContext(2)]
public static bool operator !=(SimpleType? r1, SimpleType? r2)
{
return !(r1 == r2);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public static bool operator ==(SimpleType? r1, SimpleType? r2)
{
return (object)r1 == r2 || (r1?.Equals(r2) ?? false);
}
public override int GetHashCode()
{
return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<int>.Default.GetHashCode(Value);
}
public override bool Equals(object? obj)
{
return Equals(obj as SimpleType);
}
public virtual bool Equals(SimpleType? other)
{
return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<int>.Default.Equals(Value, other!.Value);
}
public virtual SimpleType <Clone>$()
{
return new SimpleType(this);
}
protected SimpleType(SimpleType original)
{
Value = original.Value;
}
public void Deconstruct(out int Value)
{
Value = this.Value;
}
}
Interesting...if that's the generated code it should just work. Guess I'll just have to do a little debugging.
Is the additional protected
constructor tripping it up?
Oh yeah that's it. :thinking: how best to handle that. Oh wait can just filter on the public one without breaking things I think.
It looks like the simple fix in the c9-records
branch will do it, but I have to get my computer fully working with net5.0 before I release it.
This should work in v6.3.4. There may be a few other scenarios where we need some tweaks but I think it should all work. Let me know how it goes.
That works perfectly, thanks!
Closing then.
Support for C# 9 records
C# 9 introduced records, which is a concise way to write a POCO. Since we use a lot of simple types to read data using Insight.Database, this would be a heavily used use-case for us.
Sample code:
Program.cs:
And the project file:
Right now, this program fails with the following error: