C# 中的数组如何部分实现 IList<T>? [英] How do arrays in C# partially implement IList<T>?

查看:45
本文介绍了C# 中的数组如何部分实现 IList<T>?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如您所知,C# 中的数组实现了 IList 以及其他接口.但是不知何故,他们在没有公开实现 IList 的 Count 属性的情况下做到了这一点!数组只有一个 Length 属性.

So as you may know, arrays in C# implement IList<T>, among other interfaces. Somehow though, they do this without publicly implementing the Count property of IList<T>! Arrays have only a Length property.

这是 C#/.NET 违反自己的关于接口实现的规则的公然例子还是我遗漏了什么?

Is this a blatant example of C#/.NET breaking its own rules about the interface implementation or am I missing something?

推荐答案

根据汉斯的回答新的答案

感谢 Hans 给出的答案,我们可以看到实现比我们想象的要复杂一些.编译器和 CLR 都非常努力试图给人一种数组类型实现了 IList 的印象——但是数组变化使这变得更加棘手.与 Hans 的回答相反,数组类型(无论如何都是一维的,从零开始)确实直接实现了泛型集合,因为任何特定数组的类型不是 System.Array - 这只是数组的 base 类型.如果你问一个数组类型它支持什么接口,它包括泛型类型:

Thanks to the answer given by Hans, we can see the implementation is somewhat more complicated than we might think. Both the compiler and the CLR try very hard to give the impression that an array type implements IList<T> - but array variance makes this trickier. Contrary to the answer from Hans, the array types (single-dimensional, zero-based anyway) do implement the generic collections directly, because the type of any specific array isn't System.Array - that's just the base type of the array. If you ask an array type what interfaces it supports, it includes the generic types:

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.C# 规范的第 12.1.2 节是这样说的.因此,无论底层实现做什么,语言都必须表现,就好像 T[] 的类型实现了 IList界面.从这个角度来看,接口实现的,其中一些成员被显式实现(例如Count).这是语言层面对正在发生的事情的最好解释.

For single-dimensional, zero-based arrays, as far as the language is concerned, the array really does implement IList<T> too. Section 12.1.2 of the C# specification says so. So whatever the underlying implementation does, the language has to behave as if the type of T[] implements IList<T> as with any other interface. From this perspective, the interface is implemented with some of the members being explicitly implemented (such as Count). That's the best explanation at the language level for what's going on.

请注意,这仅适用于一维数组(和基于零的数组,而不是 C# 作为一种语言对非基于零的数组有任何说明).T[,] 没有实现IList.

Note that this only holds for single-dimensional arrays (and zero-based arrays, not that C# as a language says anything about non-zero-based arrays). T[,] doesn't implement IList<T>.

从 CLR 的角度来看,一些更时髦的事情正在发生.您无法获得通用接口类型的接口映射.例如:

From a CLR perspective, something funkier is going on. You can't get the interface mapping for the generic interface types. For example:

typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))

例外:

Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.

那为什么奇怪呢?好吧,我相信这真的是由于数组协方差,这是类型系统 IMO 中的一个问题.即使 IList 不是协变(并且不能安全),数组协变允许这样做:

So why the weirdness? Well, I believe it's really due to array covariance, which is a wart in the type system, IMO. Even though IList<T> is not covariant (and can't be safely), array covariance allows this to work:

string[] strings = { "a", "b", "c" };
IList<object> objects = strings;

...这使得它看起来就像 typeof(string[]) 实现了 IList,但实际上并没有.

... which makes it look like typeof(string[]) implements IList<object>, when it doesn't really.

CLI 规范 (ECMA-335) 第 1 部分第 8.7.1 节包含以下内容:

The CLI spec (ECMA-335) partition 1, section 8.7.1, has this:

签名类型 T 与签名类型 U 兼容,当且仅当以下至少一项成立

A signature type T is compatible-with a signature type U if and only if at least one of the following holds

...

T是从零开始的rank-1数组V[]UIList,V是数组-element-compatible-with W.

T is a zero-based rank-1 array V[], and U is IList<W>, and V is array-element-compatible-with W.

(它实际上没有提到 ICollectionIEnumerable 我认为这是规范中的一个错误.)

(It doesn't actually mention ICollection<W> or IEnumerable<W> which I believe is a bug in the spec.)

对于非差异,CLI 规范直接与语言规范一致.来自分区 1 的第 8.9.1 节:

For non-variance, the CLI spec goes along with the language spec directly. From section 8.9.1 of partition 1:

此外,创建的元素类型为 T 的向量实现接口 System.Collections.Generic.IList,其中 U := T. (§8.7)

Additionally, a created vector with element type T, implements the interface System.Collections.Generic.IList<U>, where U := T. (§8.7)

(vector 是一个以零为底的一维数组.)

(A vector is a single-dimensional array with a zero base.)

现在就实现细节而言,显然 CLR 正在做一些时髦的映射来保持这里的赋值兼容性:当一个 string[] 被要求用于实现时ICollection.Count,它不能以相当的正常方式处理它.这算作显式接口实现吗?我认为这样对待它是合理的,因为除非您直接要求接口映射,否则从语言的角度来看它总是行为.

Now in terms of the implementation details, clearly the CLR is doing some funky mapping to keep the assignment compatibility here: when a string[] is asked for the implementation of ICollection<object>.Count, it can't handle that in quite the normal way. Does this count as explicit interface implementation? I think it's reasonable to treat it that way, as unless you ask for the interface mapping directly, it always behaves that way from a language perspective.

ICollection.Count 怎么样?

What about ICollection.Count?

到目前为止,我已经讨论了通用接口,但还有非通用的 ICollection 及其 Count 属性.这次我们可以得到接口映射,实际上接口是通过System.Array.ICollection.Count 属性的文档Array 中的实现声明它是通过显式接口实现实现的.

So far I've talked about the generic interfaces, but then there's the non-generic ICollection with its Count property. This time we can get the interface mapping, and in fact the interface is implemented directly by System.Array. The documentation for the ICollection.Count property implementation in Array states that it's implemented with explicit interface implementation.

如果有人能想到这种显式接口实现与普通"显式接口实现的不同之处,我很乐意进一步研究.

If anyone can think of a way in which this kind of explicit interface implementation is different from "normal" explicit interface implementation, I'd be happy to look into it further.

关于显式接口实现的旧答案

尽管上面的内容由于数组的知识而更加复杂,但您仍然可以通过显式接口实现.

Despite the above, which is more complicated because of the knowledge of arrays, you can still do something with the same visible effects through explicit interface implementation.

这是一个简单的独立示例:

Here's a simple standalone example:

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
    }
}

这篇关于C# 中的数组如何部分实现 IList&lt;T&gt;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆