根据汉斯回答的新答案
感谢 Hans 给出的答案,我们可以看到实现比我们想象的要复杂一些。编译器和 CLR 都尝试非常努力给人一种数组类型实现 IList<T> 的印象 - 但数组差异使这变得更加棘手。与 Hans 的答案相反,数组类型(一维,无论如何都是从零开始的)确实直接实现了泛型集合,因为任何特定数组的类型 不是 System.Array - 这只是数组的 base 类型。如果你问一个数组类型它支持什么接口,它包括泛型类型:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
输出:
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
对于从零开始的一维数组,就语言而言,该数组确实也实现了IList<T>。 C# 规范的第 12.1.2 节是这样说的。因此,无论底层实现做什么,该语言都必须表现,就好像T[] 的类型实现IList<T> 与任何其他接口一样。从这个角度来看,接口是 实现的,其中一些成员被显式实现(例如Count)。这是在 语言 级别上对正在发生的事情的最佳解释。
请注意,这仅适用于一维数组(以及从零开始的数组,而不是 C# 作为一种语言对非从零开始的数组有任何说明)。 T[,] 没有实现IList<T>。
从 CLR 的角度来看,正在发生一些更时髦的事情。您无法获取通用接口类型的接口映射。例如:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
给出以下例外:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
那么为什么会出现这种奇怪现象呢?好吧,我相信这真的是由于数组协方差,这是类型系统中的一个缺陷,IMO。即使IList<T> 是非协变的(并且不能安全),数组协方差允许它工作:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
...这使得它看起来像 typeof(string[]) 实现 IList<object>,但实际上并非如此。
CLI 规范 (ECMA-335) 分区 1,第 8.7.1 节有:
签名类型 T 与签名类型 U 兼容当且仅当以下至少一项成立时
...
T 是从零开始的 rank-1 数组 V[],U 是 IList<W>,V 是与 W 兼容的数组元素。
(它实际上并没有提到ICollection<W> 或IEnumerable<W>,我认为这是规范中的一个错误。)
对于不变性,CLI 规范直接与语言规范一起使用。来自分区 1 的第 8.9.1 节:
此外,一个元素类型为 T 的已创建向量实现了接口 System.Collections.Generic.IList<U>,其中 U := T。(第 8.7 节)
(向量是一个零基数的一维数组。)
现在就实现细节而言,显然 CLR 正在做一些时髦的映射以保持此处的分配兼容性:当要求 string[] 实现 ICollection<object>.Count 时,它可以't 以正常方式处理它相当。这算作显式接口实现吗?我认为这样处理它是合理的,因为除非您直接要求接口映射,否则从语言的角度来看,它总是表现。
ICollection.Count 呢?
到目前为止,我已经讨论了泛型接口,但还有非泛型 ICollection 及其 Count 属性。这次我们可以得到接口映射,其实接口是直接由System.Array实现的。 Array 中的 ICollection.Count 属性实现文档声明它是通过显式接口实现实现的。
如果有人能想到这种显式接口实现与“普通”显式接口实现不同的方式,我很乐意进一步研究。
关于显式接口实现的旧答案
尽管上面因为数组的知识而比较复杂,但是你仍然可以通过explicit interface implementation做同样的可见效果。
这是一个简单的独立示例:
public interface IFoo
{
void M1();
void M2();
}
public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}
// Implicit interface implementation
public void M2() {}
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.M1(); // Compile-time failure
foo.M2(); // Fine
IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}