c# 9.0 records - reflection and generic constraints
- How do I recognize a record using reflection ?
If you try record classes in sharplab.io
you'll see that record classes are usual classes that implement IEquatable<T>
interface and contain additional members that are used to compare and clone instances of the record class. There is no special attributes that indicate that the class is a record class
.
So I guess that there is no way to determine if a class is a record class using reflection.
looking here maybe there is a way to detect the
EqualityContract
but I am not sure if that is the way to go ?
It is possible to determine using reflection if a class has such property, but this is not a 100% guarantee that the class with such property is a record class.
- Is it possible to have a generic constraint that a generic type is a record ? that is if it is possible to indicate that type parameter T must be a record class using a constraint ?
It is not possible.
- Records proposal page does not contain any information about specifying that a generic type parameter
T
must be a record class. - If you read discussion under this comment at
Champion records
page you'll learn that there is no way to specify something likewhere T : record
inC# 9
. Moreover there are plans to eliminate any meaningful semantic difference between a record and a class inC# 10
. So that records' features likewith
will be available for classes too. Addingrecord
constraint will make this goal not achievable.
As a 'hack', all records have a synthesized method <Clone>$
which you can look for. Since you can't write a method with that name in C#, a class with a <Clone>$
member is guaranteed to be a record as of C# 9.
However there is no guarantee that this will continue to be the case. For example it's possible that in C# 10.0 some records won't have a <Clone>$
member, or that some non-records will.
public static bool IsRecord(Type type) => type.GetMethod("<Clone>$") != null;
How do I recognize a record using reflection ?
As pointed out here and here
There is not only not an official way to do this, it is explicitly against the design of the feature. The intent for records is that, hopefully with C# 10, we'll get to a point where making a class a record is purely a convenience choice, and that every other part of the feature will be achievable through some form of syntax. It should not be a breaking change to change a type from a record to a class, and we even imagine that an IDE refactoring could automatically move a type to and from record syntax without clients noticing. For C# 9 there are some places where we didn't quite achieve this, but that's the goal.
Despite the above there are still scenarios where checking for record us useful. Some hackish ways to detect records which work ATM are:
- check if there is an
EqualityContract
property with theCompilerGenerated
attribute
isRecord = ((TypeInfo)t).DeclaredProperties.Where(x => x.Name == "EqualityContract").FirstOrDefault()?.GetMethod?.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is object;
- check for
<Clone>$
member as pointed out by @Yair Halberstadt
isRecord = t.GetMethod("<Clone>$") is object;
or a combination of both
Is it possible to have a generic constraint that a generic type is a record ?
No