【问题标题】:Why can't I use a compatible concrete type when implementing an interface为什么我在实现接口时不能使用兼容的具体类型
【发布时间】:2013-03-07 14:30:36
【问题描述】:

我希望能够做这样的事情:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    public interface IFoo
    {
        IEnumerable<int> integers { get; set; }
    }

    public class Bar : IFoo
    {
        public List<int> integers { get; set; }
    }
}

为什么编译器会报错..?

Error   2   'Test.Bar' does not implement interface member 'Test.IFoo.integers'. 'Test.Bar.integers' cannot implement 'Test.IFoo.integers' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable<int>'.

我知道接口说 IEnumerable 并且该类使用 List,但是 List 一个 IEnumerable.....

我能做什么?我不想在类中指定 IEnumerable,我想使用实现 IEnumerable 的具体类型,例如 List...

【问题讨论】:

  • “我想使用实现 IEnumerable 的具体类型,比如 List...” -- 为什么?

标签: c# class interface


【解决方案1】:

除非您在幕后进行,否则您将无法使用具体类型。问题是您可以同时获取和设置属性。

您的界面指定属性的类型为IEnumerable&lt;int&gt;HashSet&lt;int&gt; 实现 IEnumerable&lt;int&gt;。这意味着以下应该可以正常工作:

IFoo instance = new Bar();
instance.integers = new HashSet<int>();

但是由于您尝试使用具体类型List&lt;int&gt; 来实现接口,因此分配无法工作。

假设您不需要经常重新分配集合,最简单的解决方法是只为集合指定一个 getter:

public interface IFoo
{
    IEnumerable<int> Integers { get; }
}

public class Bar
{
    public List<int> Integers { get; private set; }

    public Bar(List<int> list)
    {
        Integers = list;
    }
}

【讨论】:

  • 我认为这可行(我现在无法测试),但我不明白为什么。为什么从接口中删除设置器会允许List&lt;int&gt; 实现IEnumerable&lt;int&gt;?编译器是否在每个方向上进行类型检查(即您可以用IEnumerable&lt;int&gt; Integers { private get; set } 实现List&lt;int&gt; Integers { set; }
  • 我假设示例代码中的Bar 实现了IFoo?如果是这样,代码不能解决问题。相同的编译错误:Test.Bar.Integers' cannot implement Test.IFoo.Integers' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable&lt;int&gt;'。我错过了什么吗?
  • 我认为Bar 中的第一行实际上应该是public IEnumerable&lt;int&gt; Integers { get; private set; },这样它就有一个与接口匹配的类型,并且受类的约束只能是一个列表。在 Bar 类上,该属性仍将是 IEnumerable&lt;int&gt;List&lt;int&gt; 约束实际上只存在于底层。除了Bar 之外,没有任何东西可以保证调用者会得到List&lt;int&gt;
【解决方案2】:

虽然 List 实现了 IEnumerable,但这不是接口的工作方式。该接口准确地指定了哪些类型需要为属性公开。如果你创建了一个像

这样的通用接口
public interface IFoo<T> where T : IEnumerable<int>
{
    T integers { get; set; }
}

然后您可以使用IFoo&lt;List&lt;int&gt;&gt; 以您期望的方式实现它。

【讨论】:

    【解决方案3】:

    这是一个类型协方差/逆变问题(请参阅http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#C.23)。

    有一个解决方法:使用显式接口,如下所示:

    public class Bar : IFoo {
    
        private IList<int> _integers;
    
        IEnumerable<int> IFoo.integers {
            get { return _integers };
            set { _integers = value as IList<int>; }
        }
    
        public IList<int> integers {
            get { return _integers; }
            set { _integers = vale; }
        }
    }
    

    请注意,integers 应为 TitleCased 以符合 .NET 的准则。

    希望您能在上面的代码中看到问题:IList&lt;int&gt;IEnumerable&lt;int&gt; 兼容,仅适用于访问器,不适用于设置。如果有人打电话给IFoo.integers = new Qux&lt;int&gt;()Qux : IEnumerable&lt;int&gt;不是Qux : IList&lt;int&gt;)会发生什么。

    【讨论】:

    • 这在处理将 JSON 对象反序列化为必须具有接口参数的具体对象的问题时非常有用。通过提供具体的参数,反序列化器可以找到一种方法来实例化参数的对象。
    猜你喜欢
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 2013-06-09
    • 1970-01-01
    • 2013-09-30
    • 1970-01-01
    相关资源
    最近更新 更多