【问题标题】:Why is the following Code Compiling and Executing successfully?为什么下面的代码编译执行成功?
【发布时间】:2014-03-13 12:05:23
【问题描述】:

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

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

注意事项:

  • 没有引用其他 IReadonlyCollection。所以它必须使用我的(IReadonlyCollection 已添加到 .Net4.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
    }

【问题讨论】:

  • 可能你有一些新的编译器,它知道如何将数组作为只读集合使用,它会生成一些额外的代码来转换它。您可以通过使用 ildasm util 验证已编译类的 IL 来检查它
  • 您能否仔细检查您实际针对的是哪个平台?我无法重现这个。 (VS2010,Fx 3.5)
  • @HenkHolterman VisualStudio 2012。我选择了一个 .Net3.5 控制台应用程序
  • 另外作为一个思路,能不能在最后加上Console.WriteLine(bar.GetType())?我不确定它会输出 System.Array
  • @rudimenter 正如 ken2k 的回答所涵盖的那样,您似乎只是偶然发现了秘密编译器短语,它可以让您在语言/工具支持赶上之前做这种事情:-) Jon Skeet 做到了async/await.

标签: c# arrays .net-3.5 implicit-conversion


【解决方案1】:

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

  • 我可以使用 VS 2012 和 .Net 3.5 重现您的问题。
  • 我无法用 VS 2010、.Net 3.5 和相同的代码重现它。

所以区别真的是编译器版本。

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

所以我猜这是另一个使用数组的黑魔法。例如见this Hans Passant answer:

编译器和 CLR 都具有数组类型的特殊知识, 就像他们对值类型所做的那样。编译器会看到您的尝试 投射到 IList 并说“好的,我知道该怎么做!”。

【讨论】:

  • 这可能与允许您使用编译器预期的方法名称等手动实现 async/await 的情况相同。还有另一个我不记得的例子,Jon Skeet 做了这种事情作为学习练习和有关它的博客。
猜你喜欢
  • 2015-10-14
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 2016-02-09
  • 1970-01-01
相关资源
最近更新 更多