接口实现理念

Interface Implementation Philosophy

在 C# 中,您不允许实现这样的接口:

public interface ITest
{
    IEnumerable<int> Numbers { get; set; }
}

public class CTest : ITest
{
    public List<int> Numbers { get; set; }
}

我的问题是:是否有某种软件哲学使这种类型的实现出错?它是否是一个有争议的范式,有点像多重继承,争论已经持续了几十年?

我看不出为什么这样的接口实现不能很好地工作。如果您知道 ITest,那么您只能看到 CTest.NumbersIEnumerable<int> 部分。如果您了解 CTest,那么您可以使用整个 List<int> 实现。既然 List<int> 派生自 IEnumerable<int>,为什么它不能满足接口要求?

调用覆盖基方法时子类 return 子类型的能力 return type covariance

在这种情况下,由于 Numbers 属性 的 setter,您的示例不安全:

ITest t = new CTest();
t.Numbers = new HashSet<int>();

如果在 ITest 中只定义了一个 getter 那么它是安全的,尽管 C# 仍然不允许它 - 但是 Java 允许:

public interface Test {
    Iterable<Integer> getNumbers();
}

public class CTest implements Test {
    public List<Integer> getNumbers() {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        return numbers;
    }
}

接口是一个合同。它告诉客户确切什么methods/properties可用,确切输入和输出的类型。

当您告诉客户任何实现 ITest 的 class 将接受任何 IEnumerable<int> 作为 Numbers 的值,但 CTest class 只会接受 List<int>,你违反了那个合同。

如果你想实现接口但也公开更具体的集合类型,你可以显式实现接口,但是你必须决定做什么是客户端尝试"set" IEnumerable<int> 而不是 List<int> 一种选择是创建一个 List<int> 幕后:

public class CTest : ITest
{
    public List<int> Numbers { get; set; }

    // satisfies the interface
    IEnumerable<int> ITest.Numbers { 
        get { return Numbers; } 
        set { Numbers = new List<int>(value); } 
    }

}

现在知道 CTest 的客户端可以使用 List<int> 属性,但只知道接口的客户端仍然可以设置集合而无需转换。