c# - 数组中自动实现的接口(interface)

标签 c# clr

我读了一本书“CLR via C# Fourth Edition”。我无法理解一种说法:

So, for example, if you have the following line of code:

FileStream[] fsArray;

then when the CLR creates the FileStream[] type, it will cause this type to automatically implement the IEnumerable<FileStream>, ICollection<FileStream>, and IList<FileStream> interfaces. Furthermore, the FileStream[] type will also implement the interfaces for the base types: IEnumerable<Stream>, IEnumerable<Object>, ICollection<Stream>, ICollection<Object>, IList<Stream>, and IList<Object>.

我用这段代码测试了这条语句:

FileStream[] fsArray = new FileStream[0];

string s = null;
foreach (var m in fsArray.GetType().GetInterfaces())
    s += m.ToString() + Environment.NewLine;

结果,我得到了这个:

System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.IO.FileStream]
System.Collections.Generic.ICollection`1[System.IO.FileStream]
System.Collections.Generic.IEnumerable`1[System.IO.FileStream]
System.Collections.Generic.IReadOnlyList`1[System.IO.FileStream]
System.Collections.Generic.IReadOnlyCollection`1[System.IO.FileStream]

没有执行 IEnumerable<Stream>和别的! 我在某处弄错了吗?还是 Jeffrey Richter 弄错了?

此外,我认为它是无意义的。因为数组支持协方差。

最佳答案

There is no implementation of IEnumerable and others!

没有。然而,IList<Stream> streamList = fsArray;将工作。你可以使用 streamList正如您所期望的那样,尽管如果您尝试对数组执行无效的操作会出现运行时异常(只要该数组是从零开始的并且具有单一维度——Microsoft 的说法是“SZ 数组”——否则它是不允许的) .

想看更糟的东西吗?

var listMap = typeof(List<FileStream>).GetInterfaceMap(typeof(IList<FileStream>)); // This works fine.
var arrMap = typeof(typeof(FileStream[]).GetInterfaceMap(typeof(IList<FileStream>)); // This throws `ArgumentException`

所以在这方面,FileStream[]甚至没有实现 IList<FileStream> ;如果确实如此,那么上面的行肯定应该有效。

从 .NET 4.0 开始,我们得到了一条有趣的线索。在此之前,ArgumentException会有 "Interface not found" 的消息,就像我们试图在 int 上获取该接口(interface)一样或 string[] .现在是"Interface maps for generic interfaces on arrays cannot be retrived." [原文如此]

如果我们尝试获取 IList<Stream> 的接口(interface)映射,它也会给我们这个但不适用于完全不受支持的接口(interface),如 IList<bool> .

这里发生了一些不寻常的事情。

它是什么 FileStream[]根本不直接支持任何通用接口(interface),与 class 相同或 struct会的。

取而代之的是一个名为 SZArrayHelper 的 stub 类它在运行时为从零开始的一维数组提供这些接口(interface)。关于 .NET Core Version 的评论信息丰富:

//----------------------------------------------------------------------------------------
// ! READ THIS BEFORE YOU WORK ON THIS CLASS.
// 
// The methods on this class must be written VERY carefully to avoid introducing security holes.
// That's because they are invoked with special "this"! The "this" object
// for all of these methods are not SZArrayHelper objects. Rather, they are of type U[]
// where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will
// see a lot of expressions that cast "this" "T[]". 
//
// This class is needed to allow an SZ array of type T[] to expose IList<T>,
// IList<T.BaseType>, etc., etc. all the way up to IList<Object>. When the following call is
// made:
//
//   ((IList<T>) (new U[n])).SomeIListMethod()
//
// the interface stub dispatcher treats this as a special case, loads up SZArrayHelper,
// finds the corresponding generic method (matched simply by method name), instantiates
// it for type <T> and executes it. 
//
// The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be
// array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly
// "T[]" - for orefs, it may be a "U[]" where U derives from T.)
//----------------------------------------------------------------------------------------

这就是发生的事情。如果你尝试投 fsArrayIList<Stream>然后你让这个类(class)为你打电话。如果您调用 GetInterfaces()您会得到类似的 stub 代码,仅提供与数组类型相关的代码。在任何一种情况下fsArray 确实实现了您引用的书中提到的所有接口(interface),但它的实现方式与class 不同。或 struct可以。

(类推一下 int 如何既可以是 32 位值的四个字节又可以是具有接口(interface)实现、方法覆盖等的“完整”对象)

所以这本书是正确的,但您也没有遗漏任何东西,因为当类型实现接口(interface)时我们期望发生的某些事情并没有发生。

Furthermore, I think it is mean-less. Because Arrays support co-variance.

支持协方差并不意味着他们会实现给定的接口(interface),反之亦然。特别是因为数组的(可以说是损坏的)协变与接口(interface)中的协变非常不同,并且早于它,而且实际上让数组实现通用接口(interface)也早于接口(interface)协变。

但是,决定FileStream[]确实应该实现 Stream[]确实与数组协变有关(否则这个决定会非常错误),但它需要额外的帮助 SZArrayHelper提供,而不是被它自动包含。

关于c# - 数组中自动实现的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31832806/

相关文章:

c# - 在数据库中存储具有公共(public)基类的对象

c# - 在母版页的代码后面更改样式表

.net - 装配中每个 NetModule 的 PE header

c# - .NET 事件特殊方法(添加/删除/提升/其他)

c# - 在 C# 中使用静态方法实现接口(interface)

c# - 在 C# 中循环遍历 DataTable 中的行并提取元素

c# - WCF - 是否可以使用 HttpListener 或套接字监听 WCF ChannelFactory

c# - 这个代码实现的静态初始化顺序是否具体?

.net - GC.Collect 与否?

c++ - NO 带有字符串数组的存储类或类型说明符