我读了一本书“通过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支持协方差。
答案 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
根本不直接支持任何通用接口,就像struct
或SZArrayHelper
一样。
相反,有一个名为//----------------------------------------------------------------------------------------
// ! 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中的此行为的更多信息。