在C#中,不允许实现这样的界面:
public interface ITest
{
IEnumerable<int> Numbers { get; set; }
}
public class CTest : ITest
{
public List<int> Numbers { get; set; }
}
我的问题是:是否有一些软件理念使这种类型的实现错误?它是一种有争议的范式,有点像多重继承,其中争论已经发生了数十年?
我无法理解为什么这样的界面实现不能正常工作的原因。如果您了解ITest
,那么您只能看到IEnumerable<int>
的{{1}}部分。如果您了解CTest.Numbers
,那么您可以使用整个CTest
实施。由于List<int>
来自List<int>
,为什么它不能满足接口要求?
答案 0 :(得分:9)
调用覆盖基本方法时子类返回子类型的能力return type covariance
在这种情况下,由于Numbers
属性的设置器
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;
}
}
答案 1 :(得分:3)
接口是合同。它告诉客户完全哪些方法/属性可用,完全输入和输出的类型。
当您告诉客户端任何实现ITest
的类将接受任何IEnumerable<int>
作为Numbers
的值时,CTest
类只会接受List<int>
1}},你违反了合同。
如果你想实现界面但是也暴露了一种更具体的集合类型,你可以显式地实现界面,但是你必须决定做什么是客户试图&#34;设定&#34;一个不 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>
属性,但只知道接口的客户端仍然可以设置集合而不进行转换。