可能是 Visual Studio 2015 中的 C# 编译器错误 [英] Maybe a C# compiler bug in Visual Studio 2015

查看:37
本文介绍了可能是 Visual Studio 2015 中的 C# 编译器错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为这是一个编译器错误.

I think this is a compiler bug.

以下控制台应用程序在使用 VS 2015 编译时可以完美地编译和执行:

The following console application compiles und executes flawlessly when compiled with 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.

But now it's getting weird: This code compiles, but it throws a TypeLoadException when executed.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var x = MyStruct.Empty;
        }

        public struct MyStruct
        {
            public static readonly MyStruct? Empty = null;
        }
    }
}

您是否遇到过同样的问题?如果是这样,我将向 Microsoft 提交问题.

Do you experience the same issue? If so, I will file an issue at Microsoft.

代码看起来毫无意义,但我用它来提高可读性并消除歧义.

The code looks senseless, but I use it to improve readability and to achieve disambiguation.

我有不同重载的方法,例如

I have methods with different overloads like

void DoSomething(MyStruct?arg1, string arg2)

void DoSomething(string arg1, string arg2)

以这种方式调用方法...

Calling a method this way...

myInstance.DoSomething(null, "Hello world!")

... 无法编译.

打电话

myInstance.DoSomething(default(MyStruct?), "Hello world!")

myInstance.DoSomething((MyStruct?)null, "Hello world!")

有效,但看起来很丑.我更喜欢这样:

works, but looks ugly. I prefer it this way:

myInstance.DoSomething(MyStruct.Empty, "Hello world!")

如果我把 Empty 变量放到另一个类中,一切正常:

If I put the Empty variable into another class, everything works okay:

public static class MyUtility
{
    public static readonly MyStruct? Empty = null;
}

奇怪的行为,不是吗?

我在这里开了一张票:http://github.com/dotnet/roslyn/issues/10126

这里开了一张新票:https://github.com/dotnet/coreclr/问题/4049

推荐答案

这不是 2015 年的错误,而是可能是 C# 语言的错误.下面的讨论涉及为什么实例成员不能引入循环,以及为什么 Nullable 会导致这个错误,但不应该适用于静态成员.

This is not a bug in 2015 but a possibly a C# language bug. The discussion below relates to why instance members cannot introduce loops, and why a Nullable<T> will cause this error, but should not apply to static members.

我会将其作为语言错误提交,而不是编译器错误.

I would submit it as a language bug, not a compiler bug.

在 VS2013 中编译此代码会出现以下编译错误:

Compiling this code in VS2013 gives the following compile error:

System.Nullable"类型的结构成员ConsoleApplication1.Program.MyStruct.Empty"导致结构布局中出现循环

Struct member 'ConsoleApplication1.Program.MyStruct.Empty' of type 'System.Nullable' causes a cycle in the struct layout

快速搜索会出现这个答案,其中指出:

A quick search turns up this answer which states:

将自身包含为成员的结构是不合法的.

It's not legal to have a struct that contains itself as a member.

不幸的是,用于值类型的可为空实例的 System.Nullable 类型也是值类型,因此必须具有固定大小.很容易将 MyStruct? 视为引用类型,但实际上并非如此.MyStruct? 的大小是基于 MyStruct 的大小...这显然在编译器中引入了一个循环.

Unfortunately the System.Nullable<T> type which is used for nullable instances of value types is also a value type and must therefore have a fixed size. It's tempting to think of MyStruct? as a reference type, but it really isn't. The size of MyStruct? is based on the size of MyStruct... which apparently introduces a loop in the compiler.

举个例子:

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? 不是引用而是比 Struct1 长 4 个字节(标准填充大小)的结构体.

Using System.Runtime.InteropServices.Marshal.SizeOf() you'll find that Struct2 is 16 bytes long, indicating that Struct1? is not a reference but a struct that is 4 bytes (standard padding size) longer than Struct1.

为了回应 Julius Depulla 的回答和评论,以下是当您访问 static Nullable 字段时实际发生的事情.从这个代码:

In response to Julius Depulla's answer and comments, here is what is actually happening when you access a static Nullable<T> field. From this code:

public struct foo
{
    public static int? Empty = null;
}

public void Main()
{
    Console.WriteLine(foo.Empty == null);
}

这是从 LINQPad 生成的 IL:

Here is the generated IL from LINQPad:

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是一个结构而不是一个引用类型.

The first instruction gets the address of the static field foo.Empty and pushes it on the stack. This address is guaranteed to be non-null as Nullable<Int32> is a structure and not a reference type.

接下来调用 Nullable 隐藏成员函数 get_HasValue 以检索 HasValue 属性值.这不会导致空引用,因为如前所述,值类型字段的地址必须是非空的,无论地址中包含的值如何.

Next the Nullable<Int32> hidden member function get_HasValue is called to retrieve the HasValue property value. This cannot result in a null reference since, as mentioned previously, the address of a value type field must be non-null, regardless of the value contained at the address.

剩下的只是将结果与 0 进行比较并将结果发送到控制台.

The rest is just comparing the result to 0 and sending the result to the console.

在此过程中,任何时候都不能在类型上调用 null",无论这意味着什么.值类型没有空地址,因此对值类型的方法调用不能直接导致空对象引用错误.这就是我们不称它们为引用类型的原因.

At no point in this process is it possible to 'invoke a null on a type' whatever that means. Value types do not have null addresses, so method invocation on value types cannot directly result in a null object reference error. That's why we don't call them reference types.

这篇关于可能是 Visual Studio 2015 中的 C# 编译器错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆