代码之家  ›  专栏  ›  技术社区  ›  rudimenter

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

  •  6
  • rudimenter  · 技术社区  · 11 年前

    我在.Net 3.5、Visual Studio 2012中编译了以下代码。

    当数组被分配给我的IReadOnlyCollection时,我希望在行上得到一个错误,因为没有从array到接口的隐式转换。 它编译成功,并且不会产生任何运行时错误。

    注意事项:

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

    文件1.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;
            }
        }
    }
    

    文件2.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
        }
    
    1 回复  |  直到 11 年前
        1
  •  2
  •   Community CDub    7 年前

    我所说的都是纯粹的猜测,但它不适合作为评论,所以我还是将其作为答案发布:

    • 我可以 用VS 2012和.Net 3.5重现您的问题。
    • 我不能 用VS 2010、.Net 3.5和相同的代码复制它。

    所以,真正不同的是编译器版本。

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

    所以我猜这是阵列的另一个黑色魔术。例如,请参见 this Hans Passant answer :

    编译器和CLR都具有数组类型的特殊知识, 就像他们对待价值类型一样。编译器看到您的尝试 铸造成IList<>然后说“好吧,我知道怎么做!”。