为什么下面的代码编译和执行成功?

本文关键字:执行 成功 编译 代码 为什么 | 更新日期: 2023-09-27 17:49:53

我在。net 3.5, Visual Studio 2012中编译了以下代码。

我期望在数组被分配给我的IReadOnlyCollection时得到一个错误,因为没有定义从数组到我的接口的隐式转换。它编译成功,也没有创建任何运行时错误。

注意事项:

    没有其他IReadonlyCollection被引用。所以它必须使用我的(IReadonlyCollection被添加到.Net4.5,不存在于早期版本)
  • 当我将其重命名为IMyCollection时,它不再编译
  • 当我更改命名空间时,它不再编译

File1.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Collections.Generic
{
    public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable
    {
        int Count
        {
            get;
        }
    }
}

File2.cs:

using System.Collections.Generic;
namespace ConsoleApplication1
{
    public class Test
    {
        public Test()
        { }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test[] foo = { new Test(), new Test(), new Test() };

            IReadOnlyCollection<Test> bar = foo;
            int count = bar.Count;
        }
    }
}

这是IL代码:

   .method private hidebysig static void Main (
            string[] args
        ) cil managed 
    {
        .entrypoint
        .locals init (
            [0] class ConsoleApplication1.Test[] foo,
            [1] class System.Collections.Generic.IReadOnlyCollection`1<class ConsoleApplication1.Test> bar,
            [2] int32 count,
            [3] class ConsoleApplication1.Test[] CS$0$0000
        )
        IL_0000: nop
        IL_0001: ldc.i4.3
        IL_0002: newarr ConsoleApplication1.Test
        IL_0007: stloc.3
        IL_0008: ldloc.3
        IL_0009: ldc.i4.0
        IL_000a: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_000f: stelem.ref
        IL_0010: ldloc.3
        IL_0011: ldc.i4.1
        IL_0012: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_0017: stelem.ref
        IL_0018: ldloc.3
        IL_0019: ldc.i4.2
        IL_001a: newobj instance void ConsoleApplication1.Test::.ctor()
        IL_001f: stelem.ref
        IL_0020: ldloc.3
        IL_0021: stloc.0
        IL_0022: ldloc.0
        IL_0023: stloc.1
        IL_0024: ldloc.1
        IL_0025: callvirt instance int32 class System.Collections.Generic.IReadOnlyCollection`1<class ConsoleApplication1.Test>::get_Count()
        IL_002a: stloc.2
        IL_002b: ret
    }

为什么下面的代码编译和执行成功?

所有我说的是纯粹的猜测,但它不适合作为评论,所以我张贴它作为一个答案:

  • 我可以重现你的问题VS 2012和。net 3.5。
  • 我不能复制它与VS 2010, . net 3.5和相同的代码。

区别在于编译器的版本。

由于类名和命名空间很重要,我认为这是VS 2012+编译器中引入的硬编码规则,以支持。net 4.5引入的新类型/接口的隐式转换。

所以我猜这是数组的另一个黑魔法。请看汉斯·帕桑特的回答:

编译器和CLR都有数组类型的特殊知识,就像值类型一样。编译器看到您的尝试转换为IList<>,然后说"好吧,我知道怎么做!"。