【问题标题】:c# interface implemention - why does this not build?c# interface implementation - 为什么这不构建?
【发布时间】:2011-11-23 17:57:47
【问题描述】:

很抱歉,如果以前有人问过这个问题,但谷歌几乎不可能。我认为一个 int 数组实现了 IEnumerable,因此 Thing 应该能够实现 IThing。怎么没有?

public interface IThing
{
    IEnumerable<int> Collection { get; }
}

public class Thing : IThing
{
    public int[] Collection { get; set; }
}

注意

public class Thing : IThing
{
    public int[] Array { get; set; }
    public IEnumerable<int> Collection
    {
         get
         {
              return this.Array;
         }
    }
}

没问题。

【问题讨论】:

  • int[]IEnumerable&lt;int&gt; 的类型不同,即使它实现了 IEnumerable&lt;int&gt;
  • C# 不支持覆盖或接口的返回类型协方差。
  • 您是否只是想了解为什么不允许这样做(原因是 SLaks 的评论),或者您的目标只是在离开界面时在类上提供具体类型(int[])定义IEnumerable&lt;T&gt;?

标签: c# arrays interface ienumerable implementation


【解决方案1】:

对于要实现的接口,方法签名和返回类型必须相同,因此 int[] 可转换为 IEnumerable 的事实恐怕没有区别。

【讨论】:

    【解决方案2】:

    接口实现必须准确地实现接口。这会阻止您返回实现该接口的类型作为成员。

    如果您希望这样做,一种选择是显式地实现接口

    public interface IThing
    {
        IEnumerable<int> Collection { get; }
    }
    
    public class Thing : IThing
    {
        public int[] Collection { get; set; }
        IEnumerable<int> IThing.Collection { get { return this.Collection; } }
    }
    

    这允许类的公共 API 使用具体类型,但接口实现可以正确实现。

    例如,有了上面的,你可以写:

    internal class Test
    {
        private static void Main(string[] args)
        {
            IThing thing = new Thing { Collection = new[] { 3, 4, 5 } };
    
            foreach (var i in thing.Collection)
            {
                Console.WriteLine(i);
            }
            Console.ReadKey();
        }
    }
    

    【讨论】:

    • 在我看来,这似乎是 c# 中的必要限制。非常感谢您的回答,通过显式实现接口我可以做我想做的事。我可以使用它在部分类中实现接口,这意味着我不必编辑自动生成的代码文件。
    【解决方案3】:

    不同实现中属性的返回类型不同——返回int[]与返回IEnumerable&lt;int&gt;不同。

    就实现接口而言 - 类型必须完全匹配

    这应该可以正常工作:

    public class Thing : IThing
    {
        public IEnumerable<int> Collection { get; set; }
    }
    

    【讨论】:

      【解决方案4】:

      您的实现应该完全按原样实现接口。

      【讨论】:

        【解决方案5】:

        因为签名

        IEnumerable<int> Collection { get; }
        

        与签名不一样

        int[] Collection { get; set; }
        

        而且当你实现一个接口时,签名应该是完全一样的。

        【讨论】:

          【解决方案6】:

          这就是协变/逆变可以派上用场的地方。此功能允许您在泛型上定义输入/输出标签,并允许您执行以下操作:

          public interface IThing<out T> where T : IEnumerable<int> {
              T Collection { get; }
          }
          
          public class Thing : IThing<int[]> {
              public int[] Collection { get; set; }
          }
          

          然后这将允许您定义其他实现,然后仍然将它们作为IThing&lt;IEnumerable&lt;int&gt;&gt;s 一起使用。

          public class Thing2 : IThing<List<int>> {
              public List<int> Collection { get; set; }
          }
          
          class Program {
              static void Main() {
                  var x = new Thing();
                  var y = new Thing2();
                  new List<IThing<IEnumerable<int>>> { x, y };
              }
          }
          

          与显式接口实现相比,这种方法的优势在于您保证 IThing.Collection 与 Thing.Collection 是完全相同的方法,而对于显式实现,它们实际上是不同的方法,所以没有这样的保证。缺点当然是你必须更明确一点,所以它会让代码看起来有点“嘈杂”。

          不知道为什么 C# 编译器不能隐式地解决这个问题;我的猜测是,使用额外的检查进行编译会花费太长时间。

          【讨论】:

            【解决方案7】:

            如果您阅读Interfaces 的定义,您会注意到它说“...类的相应成员必须是公共的、非静态的,并且与接口成员具有相同的名称和签名。”

            虽然数组或列表实现了 IEnumerable,但它不是一个 IEnumerable 对象。那是你的问题。

            您的签名必须匹配才能正常工作。您会注意到public List&lt;int&gt; Collection { get; set; } 也不起作用。您将需要更改接口的属性定义,或者让您的实现返回一个IEnumerable &lt;int&gt;,就像您在第二个工作示例中所做的那样。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-07-20
              • 1970-01-01
              • 2015-11-25
              • 1970-01-01
              • 2011-11-14
              • 2012-02-09
              • 1970-01-01
              • 2019-05-18
              相关资源
              最近更新 更多