c# 9.0 记录 - 反射和通用约束

c# 9.0 records - reflection and generic constraints

关于新记录功能的两个问题:

  1. 如何使用反射识别记录?看[这里][1]也许那里 是一种检测 EqualityContract 的方法,但我不确定这是否可行?

  2. 是否可以有一个泛型约束,泛型类型是一个记录?也就是说,如果可以使用约束来指示类型参数 T 必须是记录 class ?

  1. How do I recognize a record using reflection ?

如果你尝试在 sharplab.io 中记录 classes 你会看到记录 classes 是通常的 classes 实现 IEquatable<T> 接口和包含用于比较和克隆记录 class 实例的其他成员。没有特殊属性表明 class 是 record class.

所以我想没有办法使用反射来确定class是否是一条记录class。

looking here maybe there is a way to detect the EqualityContract but I am not sure if that is the way to go ?

如果一个class有这样的属性,可以使用反射来确定,但这不是100%保证class有这样的属性一条记录 class.


  1. 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 ?

不可能。

  1. Records proposal page 不包含任何有关指定泛型类型参数 T 必须是记录 class.
  2. 的信息
  3. 如果您在 Champion records 页面阅读此 comment 下的讨论,您将了解到无法在 C# 9 中指定类似 where T : record 的内容。此外,还有计划消除记录与 C# 10 中的 class 之间任何有意义的语义差异。因此 with 等记录的功能也可用于 classes。添加 record 约束将使该目标无法实现。

作为一个'hack',所有记录都有一个合成方法<Clone>$,你可以找找。由于您不能在 C# 中编写具有该名称的方法,因此具有 <Clone>$ 成员的 class 保证是 C# 9 的记录。

但是不能保证这种情况会继续存在。例如,在 C# 10.0 中,某些记录可能没有 <Clone>$ 成员,或者某些 non-records 可能有。

public static bool IsRecord(Type type) => type.GetMethod("<Clone>$") != null;

How do I recognize a record using reflection ?

正如指出的那样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.

尽管有上述情况,在某些情况下检查记录对我们还是很有用的。检测 ATM 工作记录的一些骇人听闻的方法是:

  1. 检查是否有 EqualityContract 属性 具有 CompilerGenerated 属性
isRecord = ((TypeInfo)t).DeclaredProperties.Where(x => x.Name == "EqualityContract").FirstOrDefault()?.GetMethod?.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is object;
  1. 检查@Yair Halberstadt 指出的 <Clone>$ 成员
isRecord = t.GetMethod("<Clone>$") is object;

或两者的结合

Is it possible to have a generic constraint that a generic type is a record ?

没有

正如大家所说,写不出来

private void MyFunc<T>(T t) where T : record {...}

但是您可以创建一个记录,然后您创建的每个记录类型都继承自该记录。这几乎可以实现您的要求,尽管我不知道我对此有何感想...

public abstract record RecordMarker;
public record MyRecord : RecordMarker;
public void MyFunc<T>(T t) where T : RecordMarker
{
}

由于类无法从记录继承,因此您只能传入记录类型。

MyFunc(new MyRecord()); // Works
MyFunc(new MyClass());  // Compiler Error
isRecord = t.GetMethod("<Clone>$") is object; ==> isRecord = t.GetMethod("<Clone>$") is not null;

尽管这里有一些答案,但似乎是一个非常合理的问题。事实上,记录只是 class 的语法糖,可以通过多种其他方式创建。

一个选项是创建一个空的标记记录库 class 或接口,即使它是空的,并向其添加约束。这将实现所需的约束。

如果您从这个 classes 继承所有您希望包含在约束中的 class,那么它将正常工作。