我认为这是一个编译器错误。
当使用VS 2015进行编译时,以下控制台应用程序可以正常编译和执行:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var x = MyStruct.Empty;
}
public struct MyStruct
{
public static readonly MyStruct Empty = new MyStruct();
}
}
}
但是现在变得很奇怪:该代码可以编译,但是TypeLoadException
在执行时会抛出a 。
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var x = MyStruct.Empty;
}
public struct MyStruct
{
public static readonly MyStruct? Empty = null;
}
}
}
您是否遇到相同的问题?如果是这样,我将向Microsoft提出问题。
该代码看起来毫无意义,但我使用它来提高可读性并消除歧义。
我有不同的重载的方法,如
void DoSomething(MyStruct? arg1, string arg2)
void DoSomething(string arg1, string arg2)
以这种方式调用方法...
myInstance.DoSomething(null, "Hello world!")
...无法编译。
呼唤
myInstance.DoSomething(default(MyStruct?), "Hello world!")
或者
myInstance.DoSomething((MyStruct?)null, "Hello world!")
可以,但是看起来很丑。我更喜欢这样:
myInstance.DoSomething(MyStruct.Empty, "Hello world!")
如果我将Empty
变量放入另一个类,则一切正常:
public static class MyUtility
{
public static readonly MyStruct? Empty = null;
}
奇怪的行为,不是吗?
我在这里打开了一张票:http : //github.com/dotnet/roslyn/issues/10126
新票已在此处打开:https : //github.com/dotnet/coreclr/issues/4049
这不是2015年的错误,但可能是C#语言错误。下面的讨论涉及为什么实例成员不能引入循环,以及为什么aNullable<T>
会导致此错误,但不适用于静态成员。
我将其提交为语言错误,而不是编译器错误。
在VS2013中编译此代码会产生以下编译错误:
类型为“ System.Nullable”的结构成员“ ConsoleApplication1.Program.MyStruct.Empty”导致结构布局中的循环
快速搜索即可找到以下答案:
具有包含自身作为成员的结构是不合法的。
不幸的System.Nullable<T>
是,用于值类型的可为空的实例的类型也是一个值类型,因此必须具有固定的大小。将其MyStruct?
视为引用类型是很诱人的,但实际上并非如此。的大小MyStruct?
基于...的大小,MyStruct
这显然在编译器中引入了循环。
举个例子:
public struct Struct1
{
public int a;
public int b;
public int c;
}
public struct Struct2
{
public Struct1? s;
}
使用,System.Runtime.InteropServices.Marshal.SizeOf()
您会发现它的Struct2
长度为16个字节,这表明它Struct1?
不是引用,而是比更长4个字节(标准填充大小)的结构Struct1
。
为了响应朱利叶斯·德普拉(Julius Depulla)的回答和评论,这是您访问字段时实际发生的情况static Nullable<T>
。从此代码:
public struct foo
{
public static int? Empty = null;
}
public void Main()
{
Console.WriteLine(foo.Empty == null);
}
这是从LINQPad生成的IL:
IL_0000: ldsflda UserQuery+foo.Empty
IL_0005: call System.Nullable<System.Int32>.get_HasValue
IL_000A: ldc.i4.0
IL_000B: ceq
IL_000D: call System.Console.WriteLine
IL_0012: ret
第一条指令获取静态字段的地址,foo.Empty
并将其压入堆栈。保证该地址是非空的,就像Nullable<Int32>
结构一样,不是引用类型。
接下来,调用Nullable<Int32>
隐藏成员函数get_HasValue
以检索HasValue
属性值。如前所述,这不能导致空引用,因为值类型字段的地址必须为非空值,而与该地址中包含的值无关。
剩下的只是将结果与0进行比较,然后将结果发送到控制台。
在此过程中,无论如何都不可能“在类型上调用null”。值类型没有空地址,因此对值类型的方法调用不能直接导致空对象引用错误。这就是为什么我们不称它们为引用类型的原因。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句