任何人都可以向我解释为什么我想在C#中使用IList而不是List?
相关问题:为什么暴露它被认为是不好的List<T>
很长一段时间我都对以下内容感到好奇:
int[] array = new int[1];
int iArrayLength = array.Length; //1
Run Code Online (Sandbox Code Playgroud)
由于数组实现了IList接口,因此允许以下内容:
int iArrayCount = ((IList<int>)array).Count; //still 1
Run Code Online (Sandbox Code Playgroud)
但:
int iArrayCount = array.Count; //Compile error. WHY?
int iArrayLength = array.Length; //This is what we learned at school!
Run Code Online (Sandbox Code Playgroud)
问题:如何IList<T>在不允许在基类上使用数组的情况下实现(特别是int Count { get; }属性IList<T>)?
我知道在.NET中,所有数组都派生自System.Array并且System.Array类实现IList,ICollection和IEnumerable.实际的数组类型也实现IList<T>,ICollection<T>和IEnumerable<T>.
这意味着如果你有一个String[],那么那个String[]对象也是a System.Collections.IList和a System.Collections.Generic.IList<String>;.
不难看出为什么那些IList会被认为是"ReadOnly",但令人惊讶的是......
String[] array = new String[0];
Console.WriteLine(((IList<String>)array).IsReadOnly); // True
Console.WriteLine(((IList)array).IsReadOnly); // False!
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,尝试通过Remove()和RemoveAt()方法删除项会导致NotSupportedException.这表明两个表达式都对应于ReadOnly列表,但IList的ReadOnly属性不返回预期值.
怎么会?
我们知道System.Array是一个抽象类,无论DataType[]我们使用什么,运行时都会为我们创建一些具体的实现(虽然模糊不清).
请考虑以下代码段.
int[] someInts = { 1, 2, 3, 4 };
IList<int> collection = someInts;
collection.Clear();
Run Code Online (Sandbox Code Playgroud)
collection.Clear()抛出NotSupportedException,没有什么令人惊讶的.当我查看"StackTrace"时,我惊讶地发现它SZArrayHelper在调用堆栈顶部显示了一些奇怪的"类型" .
堆栈跟踪:
at System.SZArrayHelper.Clear[T]()//Note this.. How???
at TestApplication.Program.Main()
Run Code Online (Sandbox Code Playgroud)
怎么可能呢?我正在调用Clear()方法,int[]但是调用是如何进行的SZArrayHelper.Clear.请注意,这Clear是一个SZArrayHelper定义如下的实例方法.
private void Clear<T>()
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_ReadOnlyCollection"));
}
Run Code Online (Sandbox Code Playgroud)
谁创建了"SZArrayHelper"的实例,并注意到Clear方法是私有的.我很困惑地看到发生了什么.如果创建了一个"SZArrayHelper"实例并且Clear被调用,那么执行此调用的辅助方法应该出现在"StackTrace"中.但这不是这种情况.
有人可以解释一下幕后发生的事情吗?
注意:
int[]只是和例子,您可以使用任何类型的数组来模拟它.不仅Clear方法Add,Contains等等具有相同的行为..
我尝试使用反射器插件调试,这给出了相同的结果调试器显示直接调用SZArrayHelper.Clear<T>().
只是一个谷歌带我到这个.NET阵列,IList,通用算法,以及STL怎么样?.这有助于理解某种魔法落后但神秘仍然存在.
给出以下C#代码:
int[,] array2D = new int[10, 10];
int sum = 0;
foreach (var i in array2D)
{
sum += i;
}
Run Code Online (Sandbox Code Playgroud)
问题是:是什么导致类型i被正确推断为int?
这并不是一件容易的事,因为array2D是一个矩形阵列.它没有实现IEnumerable<int>.它还实现了一个GetEnumerator()返回a 的方法System.Collections.IEnumerator.因此,我希望这种i类型object.
我的代码使用的是.net 4.03.
我在.Net 3.5,Visual Studio 2012中编译了以下代码.
当数组被分配给我的IReadOnlyCollection时,我期望在该行上出现错误,因为没有从Array定义到我的接口的隐式转换.它编译成功,也不会创建任何运行时错误.
需要注意的注意事项:
File1.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Collections.Generic
{
public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable
{
int Count
{
get;
}
}
}
Run Code Online (Sandbox Code Playgroud)
File2.cs:
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Test
{
public Test()
{ }
}
class Program
{
static void Main(string[] args)
{
Test[] foo = { new Test(), new Test(), new Test() };
IReadOnlyCollection<Test> bar = foo;
int count = bar.Count;
} …Run Code Online (Sandbox Code Playgroud) 我意识到数组实现IEnumerable<T>"在运行时".根据数据的MSDN文档:
从.NET Framework 2.0开始,Array类实现
System.Collections.Generic.IList<T>,System.Collections.Generic.ICollection<T>以及System.Collections.Generic.IEnumerable<T>通用接口.这些实现在运行时提供给数组,因此,泛型接口不会出现在Array类的声明语法中.
当然,这是正确的.在查看源代码时,Array您会看到它仅实现了非泛型版本IEnumerable.
如果运行下面的代码片段,您将看到泛型Enumerator的类型System.SZArrayHelper+SZGenericArrayEnumerator`1[System.String].
String[] array = new[] { "Apple", "Banana", "Grape" };
IEnumerator enumer = ((IEnumerable<String>)array).GetEnumerator();
// Next line writes: System.SZArrayHelper+SZGenericArrayEnumerator`1[System.String]
Console.WriteLine(enumer.GetType());
Run Code Online (Sandbox Code Playgroud)
我希望有人能够提供一些关于 CLR 如何IEnumerable<T>在运行时实现的见解?实现这一目标的机制是什么?在CLR中是硬编码的还是Array类中有什么东西可以通知CLR动态实现泛型版本?
作为后续工作,我正在阅读"Nutshell中的C#5,0"和第274页,作者声称Array该类必须实现非IEnumerable向后版本的"向后兼容性".有人可以举例说明这种"向后兼容性"的必要性吗?
我读了一本书"通过C#第四版CLR".我无法理解一个陈述:
因此,例如,如果您有以下代码行:
Run Code Online (Sandbox Code Playgroud)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;
Run Code Online (Sandbox Code Playgroud)
结果,我有这个:
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]
Run Code Online (Sandbox Code Playgroud)
没有执行IEnumerable<Stream>和其他人!我在某处弄错了吗?或杰弗里里希特犯了错误?
此外,我认为这是无意义的.因为Arrays支持协方差.
我想知道为什么这个代码在C++/CLI中不起作用但在C#中该死的很容易?
List<Process^>^ processList = gcnew List<Process^>(
Process::GetProcessesByName(this->processName)););
Run Code Online (Sandbox Code Playgroud)
错误C2664:'System :: Collections :: Generic :: List :: List(System :: Collections :: Generic :: IEnumerable ^)':无法将参数1从'cli :: array ^'转换为'System :: Collections :: Generic :: IEnumerable ^'
这是我想出来的.做得很好.:)
List<Process^>^ processList = gcnew List<Process^>(
safe_cast<System::Collections::Generic::IEnumerable<Process^>^>
(Process::GetProcessesByName(this->processName)));
Run Code Online (Sandbox Code Playgroud) 我有一个类构造函数,它接受一个IList<IElement>参数.
在创建类的新实例时,我能够传递一个IElement[]而不是IList<IElement>那个有意义的东西?
作为示例,使用String类中的Join()方法.当你在字节数组上调用它时,例如
byte[] bytes = {3,4,5};
string str = string.Join(",", bytes);
Run Code Online (Sandbox Code Playgroud)
C#编译器映射加入此签名
public static String Join<T>(String separator, IEnumerable<T> values);
Run Code Online (Sandbox Code Playgroud)
但是,byte []从类Array中隐式派生,它不是从通用的IEnumerable派生的,而是派生自非通用的IEnumerable,即
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable {...}
Run Code Online (Sandbox Code Playgroud)
如果我对我自己的类似于这种情况的接口和类做同样的事情,我将得到预期的编译器错误,因为根据规则,你不能将从接口IA(非通用IEnumerable)派生的类转换为接口IB(来自IA的通用IEnumerable).这意味着C#编译器只需对特定名称IEnumerable进行硬编码.这在哪里解释?