在数组中自动实现接口

时间:2015-08-05 12:39:14

标签: c# clr

我读了一本书“通过C#第四版CLR”。我无法理解一个陈述:

  

因此,例如,如果您有以下代码行:

FileStream[] fsArray;
     

然后当CLR创建FileStream[]类型时,它会   导致此类型自动实现   IEnumerable<FileStream>ICollection<FileStream>和。{   IList<FileStream>接口。此外,FileStream[]类型   还将实现基类型的接口:   IEnumerable<Stream>IEnumerable<Object>,   ICollection<Stream>ICollection<Object>,   IList<Stream>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>和其他人没有实施! 我在某处弄错了吗?或杰弗里里希特犯了错误?

此外,我认为这是无意义的。因为Arrays支持协方差。

2 个答案:

答案 0 :(得分:3)

  

IEnumerable和其他没有实现!

不。然而,IList<Stream> streamList = fsArray;将起作用。您可以按照预期使用streamList,但如果您尝试在阵列上执行无效操作,则可以使用var listMap = typeof(List<FileStream>).GetInterfaceMap(typeof(IList<FileStream>)); // This works fine. var arrMap = typeof(typeof(FileStream[]).GetInterfaceMap(typeof(IList<FileStream>)); // This throws `ArgumentException` (只要数组从零开始并具有单个维度 - &# 34; SZ阵列&#34;用微软的说法 - 否则不允许)。

想要看到更糟糕的事情吗?

FileStream[]

所以在这方面,IList<FileStream>甚至没有实现ArgumentException;如果确实如此,那么上述行应该可行。

我们从.NET 4.0中获得了一个有趣的线索。在此之前,"Interface not found"会有int的消息,与我们尝试在string[]"Interface maps for generic interfaces on arrays cannot be retrived."上获取该界面的消息相同。现在是IList<Stream> [原文如此]

如果我们尝试获取IList<bool>的界面地图而不是像FileStream[]那样完全不受支持的界面,它也会给我们这个。

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

它是什么,class根本不直接支持任何通用接口,就像structSZArrayHelper一样。

相反,有一个名为//---------------------------------------------------------------------------------------- // ! 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.) //---------------------------------------------------------------------------------------- 的存根类,它在运行时为基于零的一维数组提供这些接口。对.NET Core Version的评论是提供信息的:

fsArray

那就是发生了什么。如果您尝试将IList<Stream>转换为GetInterfaces(),那么您可以让此课程为您执行调用。如果你调用fsArray,你会得到类似的存根代码,只提供与数组类型相关的存根代码。在任何一种情况下,class 都会实现您引用的书中提到的所有界面,但它不会像struct或{{{{}}那样执行此操作。 1}}可以。

(考虑类比,int如何既可以是32位值的四个字节,也可以是一个&#34;完整的&#34;对象,具有接口实现,方法覆盖等。)

所以这本书是正确的,但你也不会错过任何东西,因为当类型实现界面时,我们预期会发生一些事情。

  

此外,我认为这是无意义的。因为Arrays支持协方差。

支持协方差并不意味着他们将实现给定的接口,反之亦然。特别是因为数组&#39; (可以说是破坏的)协方差与接口中的协方差非常不同,并且在它之前,并且确实有数组实现通用接口也早于接口协方差。

然而,确定FileStream[]确实应该实现Stream[]确实与数组是协变的(否则这个决定本来就是奇怪的错误),但它需要额外的帮助{{ 1}}提供,而不是由它自动引起。

答案 1 :(得分:1)

  

因为Arrays支持协方差。

它是因为数组是协变的,它们还必须实现元素基类的通用接口。换句话说,每个人都希望这能起作用:

    var a = new FileStream[] { new FileStream("a", FileMode.Create) };
    Stream[] b = a;                 // Fine, covariant
    var iterb = (IList<Stream>)b;   // Fine of course, actually iterates FileStreams

对Stream []对象引用的赋值不以任何方式改变对象,它仍然是引擎盖下的FileStream []。因此,岩石硬盘要求是FileStream []也实现了IList<Stream>。并IList<Object>IEnumerable<Stream>,等等。

所以真正发现的是,Reflection并不能完美地模拟数组协方差。为此可以原谅。数组实际上实现实现这些接口,CLR只知道如何提供具有所需行为的替代对象。嘎嘎叫鸭子打字。有关this Q+A中的此行为的更多信息。