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

时间:2022-04-22 23:06:14

I compiled the following code in .Net 3.5, Visual Studio 2012.

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

I expected to get an error on the line when the array gets assigned to my IReadOnlyCollection, because there is no implicit conversion defined from Array to my Interface. It compiles successful and does also not create any runtime errors.

当数组被分配给我的IReadOnlyCollection时,我预计会在行上出错,因为没有从Array定义到我的接口的隐式转换。它编译成功,也不会创建任何运行时错误。

Notes to be aware of:

需要注意的注意事项:

  • There is no other IReadonlyCollection referenced. So it has to use mine (IReadonlyCollection was added to .Net4.5 and does not exist in earlier versions)
  • 没有引用其他IReadonlyCollection。所以它必须使用我的(IReadonlyCollection被添加到.Net4.5并且在早期版本中不存在)

  • When i rename it to IMyCollection it does not compile anymore
  • 当我将它重命名为IMyCollection时,它不再编译

  • When i change the namespace it does not compile anymore.
  • 当我更改名称空间时,它不再编译。

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;
        }
    }
}

This is the IL code by the way:

这是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 个解决方案

#1


2  

All I'm saying is pure guess, but it doesn't fit as a comment so I'm posting it as an answer anyway:

我所说的只是纯粹的猜测,但它不适合作为评论,因此无论如何我都将其作为答案发布:

  • I can reproduce your issue with VS 2012 and .Net 3.5.
  • 我可以使用VS 2012和.Net 3.5重现您的问题。

  • I can't reproduce it with VS 2010, .Net 3.5 and identical code.
  • 我无法使用VS 2010,.Net 3.5和相同的代码重现它。

So the difference is really the compiler version.

所以区别在于编译器版本。

As the name of the class and the namespace are important, I'm assuming it's a hardcoded rule that has been introduced in the compiler of VS 2012+, to support implicit conversion of the new types/interfaces introduced with .Net 4.5.

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

So I'm guessing it's another black magic trick with arrays. See for example this Hans Passant answer:

所以我猜它是阵列的另一个黑魔法技巧。例如,见Hans Passant答案:

Both the compiler and the CLR have special knowledge of array types, just as they do of value types. The compiler sees your attempt at casting to IList<> and says "okay, I know how to do that!".

编译器和CLR都具有数组类型的特殊知识,就像它们对值类型一样。编译器看到你试图转换为IList <>并说“好吧,我知道怎么做!”。

#1


2  

All I'm saying is pure guess, but it doesn't fit as a comment so I'm posting it as an answer anyway:

我所说的只是纯粹的猜测,但它不适合作为评论,因此无论如何我都将其作为答案发布:

  • I can reproduce your issue with VS 2012 and .Net 3.5.
  • 我可以使用VS 2012和.Net 3.5重现您的问题。

  • I can't reproduce it with VS 2010, .Net 3.5 and identical code.
  • 我无法使用VS 2010,.Net 3.5和相同的代码重现它。

So the difference is really the compiler version.

所以区别在于编译器版本。

As the name of the class and the namespace are important, I'm assuming it's a hardcoded rule that has been introduced in the compiler of VS 2012+, to support implicit conversion of the new types/interfaces introduced with .Net 4.5.

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

So I'm guessing it's another black magic trick with arrays. See for example this Hans Passant answer:

所以我猜它是阵列的另一个黑魔法技巧。例如,见Hans Passant答案:

Both the compiler and the CLR have special knowledge of array types, just as they do of value types. The compiler sees your attempt at casting to IList<> and says "okay, I know how to do that!".

编译器和CLR都具有数组类型的特殊知识,就像它们对值类型一样。编译器看到你试图转换为IList <>并说“好吧,我知道怎么做!”。