默认表达式
本文关键字:表达式 默认 | 更新日期: 2023-09-27 18:28:12
在C#规范中说,首先是:
如果默认值表达式中的类型在运行时计算为引用类型,结果为null并转换为该类型。如果类型在默认值表达式中,在运行时评估为值类型,结果是值类型的默认值(§4.1.2)。
因此,默认表达式似乎是在运行时计算的。。。但是之后说:
如果type是引用类型或已知为引用类型(§10.1.5)。此外,默认值表达式是常量表达式,如果该类型是以下值类型之一:sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double、decimal、bool或任何枚举类型。
那么,如果只检查一个运行时类型,default
怎么能是一个常量表达式,从而在编译时求值呢?
例如,如果我写这样的东西:
J k = default(J);
其中J
是类型参数,只有在运行时,当我们提供参数类型时,我们才能知道J
是引用类型还是值类型。那么编译时会发生什么呢?
您只是看错了规范(重点是我的):
- 默认值表达式是常量表达式(§7.19)如果类型是引用类型或已知为参考类型
这意味着,如果(并且仅当)类型或类型参数已知为引用类型(意味着它具有where T : class
约束或使用default(SomeClass)
),则表达式为常数。继续:
- 此外,默认值表达式是常量表达式如果类型是以下值类型之一:sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal,bool或任何枚举类型
也就是说,出于某种原因,您正在使用default(sbyte)
或default(short)
。
例如,给出以下代码:
void Main()
{
var x = default(byte);
var y = default(M);
}
public struct M { }
发射的IL将是:
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0 // x
IL_0003: ldloca.s 01 // y
IL_0005: initobj UserQuery.M
IL_000B: ret
对于byte
,编译器可以发出0,它必须为我们的M
结构调用initobj
。
它是在编译时评估的,因为J
也是,你不在运行时提供参数类型,而是在编译时提供,所以如果你有一个类foo<J>
,它包含一个方法栏,其中包含你在进行时提到的代码
foo<int> myfoo =new foo<int>(); // this is also evaluated at compile time
myfoo.bar(); // the compiler invokes the bar method of the foo<int> type and thus returns default(int)
如果默认值表达式中的类型在运行时评估为引用类型
如果类型在编译时计算为肯定引用类型或肯定不引用类型,则可以在编译时确定它在运行时计算为什么。
在具有类型参数T
的泛型方法(或泛型类的方法)中,default(string)
和default(int)
是这样,但default(T)
不是这样(除非以某种方式受到约束,意味着它只能是另一个方法中的一个)。
因此,引用类型或非引用类型的质量只能在运行时推导出来。
然而,严格地说,它实际上可以在jit时间推导出来,而且这确实已经完成了,包括由于没有编译这个逻辑而已知已经死亡的代码,所以在:
if (default(T) == null)
{
// code for reference-type or Nullable<> types of T
}
else
{
// code for non-nullable value types of T
}
只有使用的分支才会被编译成机器代码,在任何一种情况下都会跳过比较本身。