【问题标题】:Assign to interface array initializator compiles but why?分配给接口数组初始化程序编译但是为什么?
【发布时间】:2013-12-20 11:36:29
【问题描述】:

今天我在想,制作一些接口类型的匿名对象会很整洁,我在 SO 上看到我不仅仅是一个。

在我开始检查发生了什么之前,我编写了一些代码,如下所示。 令我高兴的是,它编译时使用的是 .net 框架 4,我知道没有办法让匿名对象实现接口,但我没有看到 VS 对此代码的抱怨。

当我把大括号 intelisense 找到我的界面的“属性”时,更好的是,就像它是有效的代码一样。

为什么这个片段在编译并且运行时给出空引用异常?

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            Holder holder = new Holder { someInterface = { Property = 1 } };
            Console.WriteLine(holder.someInterface.Property);
        }
    }

    class Holder
    {
        public ISomeInterface someInterface{get; set;}
    }

    interface ISomeInterface
    {
        int Property { get; set; }
    }
}

【问题讨论】:

    标签: c#


    【解决方案1】:
    Holder holder = new Holder { someInterface = { Property = 1 } };//<--Note you missed new keyword
    

    上面一行等于

    Holder temp = new Holder();
    temp.someInterface.Property = 1;
    Holder holder = temp;// <--Here someInterface is null so you get null reference exception. 
    

    这应该类似于

    Holder holder = new Holder { someInterface = new SomeClass(){ Property = 1 } };//<--Note the new keyword here
    

    注意:您的代码从未引入“匿名类型”,它是一个“对象初始化器”。

    当你使用带有 new 关键字的 ObjectInitializer 语法时,这意味着你在设置一些东西,当你使用没有 new 关键字的 ObjectInitializer 语法时,这意味着你在阅读 一些东西。

    【讨论】:

    • 好答案。仅供参考,这绝对可以通过我刚刚找到的一个很棒的第三方库来完成。 stackoverflow.com/questions/9249645/…
    • +1 为了完整起见,这在 §7.6.10.1§7.6.10.2 的 C# 5.0 规范中进行了描述。
    • @MichaelPerrenoud 这是我上周在寻找类似功能时发现的一大段代码:)
    • @Adriano 谢谢,我想知道你们如何指出规范的确切部分!有时我什至不知道该功能的确切名称是什么:(
    • 小修正,这相当于Holder temp = new Holder(); temp.someInterface.Property = 1; Holder holder = temp;。属性分配发生在 holder 初始化之前。想象一下如果holder 是一个静态字段;您不会期望该属性能够读取该字段并获取新对象,对吧?
    【解决方案2】:

    制作一些接口类型的匿名对象会很整洁,

    我知道没有办法让匿名对象实现接口,但我没有看到 VS 对这段代码的抱怨。

    问题是,您假设以下代码创建了一个匿名类型的新实例

    new Holder { someInterface = { Property = 1 } };
    

    { Property = 1 } 不会创建匿名类型的新实例 - 这是一个对象初始化器。

    如果您确实将代码替换为匿名类型的正确实例化,那么编译器将抱怨该实例无法像您预期的那样隐式转换为 ISomeInterface

    new Holder { someInterface = new { Property = 1 } };
    

    【讨论】:

      【解决方案3】:

      值得注意的是为什么编译器允许这种行为。原因是someInterface 不一定是null。这是对象初始值设定项语法被翻译成的内容:

      Holder temp = new Holder(); //creates temp object calling default constructor
      temp.someInterface = yourValue;
      holder = temp; //finally assigned back to your variable.
      

      在您的情况下,someInterface 未初始化。但是,如果您的空构造函数正确初始化 someinterface,则情况并非如此。

      class Holder
      {
          public Holder()
          {
             someInterface = new Class();
          }
      
          public ISomeInterface someInterface{get; set;}
      }
      

      现在可以了:

      Holder holder = new Holder { someInterface = { Property = 1 } };
      

      【讨论】:

        【解决方案4】:

        你必须在 Holder 构造器中为 someInterface 分配一个实例;否则为空。

        【讨论】:

        • someInterface 被初始化为无效值 - 这是“为什么编译”问题的重点?
        猜你喜欢
        • 1970-01-01
        • 2022-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-31
        • 2019-06-18
        相关资源
        最近更新 更多