【问题标题】:where t : class generic constraint and const value declaration其中 t :类通用约束和 const 值声明
【发布时间】:2013-07-22 23:35:01
【问题描述】:

根据10.4 Constants中的C#规范:

常量声明中指定的类型必须是 sbyte, byte, 短,ushort,int,uint,long,ulong,char,float,double,decimal, bool、string、枚举类型、或引用类型。每个 常量表达式必须产生目标类型或类型的值 可以通过隐式转换转换为目标类型 (§6.1)。

为什么我不能做以下事情:

public class GenericClass<T>
    where T : class
{
    public const T val = null;
}

这应该是可能的,因为:

  • where T : class 表示The type argument must be a reference type; this applies also to any class, interface, delegate, or array type(来自MSDN
  • 它满足规范中的另一个词:string 以外的引用类型常量的唯一可能值是null

有什么可能的解释吗?

【问题讨论】:

标签: c# generics constants


【解决方案1】:

可能的解释

考虑 CLR 如何初始化泛型类的 static 成员,或者何时调用泛型类型的静态构造函数。通常,静态初始化发生在程序第一次加载时;但是,泛型类在第一次创建该类的实例时会初始化其静态成员。

请记住,泛型类不是单一类型;在类型声明中传递的每个 T 都在创建一个新类型。

现在考虑一个const 表达式,它需要在编译时进行评估。虽然 T 被限制为一个类,因此它可以接收 null 值,但变量 val 在运行时创建类之前并不存在于内存中。

例如,考虑const T val 是否有效。然后在代码的其他地方我们可以使用:

GenericClass<string>.val
GenericClass<object>.val

编辑

虽然两个表达式的值都是null,但前者的类型为string,后者的类型为object。为了让编译器执行替换,它需要知道相关常量的类型定义。

约束可以在编译时强制执行,但开放泛型直到运行时才会转换为封闭泛型。因此,GenericClass&lt;object&gt;.val 不能存储在编译器的本地内存中来执行替换,因为编译器没有实例化泛型类的封闭形式,因此不知道将常量表达式实例化为什么类型。

【讨论】:

  • 你误解了静态泛型和常量。
  • 泛型类型构造函数(开放泛型类)为每个参数化创建一个单独的(封闭泛型)类型。这些类型中的每一个都有一个独立的初始化程序,它独立于其他参数化运行。这就是为什么写static readonly T MyField = new T()是合法的。
  • const 字段实际上并不存在于内存中;编译器在引用时会在编译时替换它们的值(这就是为什么它们必须是编译类型常量的原因)。编译器可以轻松区分不同封闭泛型类型的字段。
  • @SLaks,为了让编译器执行替换,它不需要知道被评估的表达式的类型吗?在定义常量的地方,它的类型为T,编译器不知道。虽然编译器能够强制约束并且T被约束为一个引用类型,但这并不意味着T本身就是一个引用类型。
  • 替换发生在常量被引用的地方,T 是已知的。 (除了,我猜,如果你写const T myConst = OtherClass&lt;T&gt;.otherConst
【解决方案2】:

Eric Lippert 承认这是一个错误,应该允许:

在我看来你发现了一个错误;要么错误在规范中,它应该明确指出类型参数不是有效类型,要么错误在编译器中,应该允许它。

【讨论】:

    猜你喜欢
    • 2011-02-27
    • 2012-02-02
    • 2017-01-21
    • 1970-01-01
    • 2012-09-28
    • 2017-04-11
    • 1970-01-01
    • 2019-03-21
    相关资源
    最近更新 更多