列表与LT; T>投射算法和性能考虑因素
-
03-07-2019 - |
题
我有以下代码
class Program
{
static void Main(string[] args)
{
List<A> aList = new List<A>();
var aObj = new A();
aObj.Go(aList.Cast<IB>());
}
}
class A : IB
{
public void Go(IEnumerable<IB> interfaceList)
{
foreach (IB ibby in interfaceList)
{
Console.WriteLine("Here");
}
}
}
interface IB
{
void Go(IEnumerable<IB> interfaceList);
}
}
我最初尝试传递一个List但是不起作用。 在所以的大量帮助后,我发现通过IEnumerable是唯一的方法是将对象作为.ofType(IB)。
不幸的是,在我的代码中,以下行将被执行数千次:
aList.Cast<IB>();
我想知道是否有人知道它是如何通过算法实现的(在IL中)以及它的时间顺序是什么。
也就是说,它是否比仅仅投射每个项目的foreach循环更快,还是正是它所做的那样?
编辑主类需要维护实际对象的列表。但是只允许读者通过界面触摸它们。
解决方案
您应该将 Go
更改为:
public void Go<T>(IEnumerable<T> interfaceList)
where T : IB
{
foreach (IB ibby in interfaceList)
{
Console.WriteLine("Here");
}
}
然后你就可以了,无需调用 Cast
。我怀疑Cast的源代码实现非常简单,虽然我认为它在3.5和3.5SP1之间变化。但是,它可能需要以正常的迭代器块方式设置新的状态机等。如果可能的话,最好避免它。
即使新方法是通用的,类型推断通常应该为您处理,因此您不需要明确指定 T
。
其他提示
为什么不直接声明列表:
List<IB> aList = new List<IB>();
是否有特别的东西需要你有一个具体类的列表?
因此,在这种情况下,我会将列表作为域的一部分。有一个像IIBCollection这样的接口(例如),公开你希望读者能够访问的方法。例如:
interface IIBCollection{
IEnumerable<IB> IBs { get; }
}
// and in your implementation you can do
IEnumerable<IB> IBs {
get {
foreach(IB ib in innerList) yield return ib;
}}
它在内部实现为CastIterator,它比转换每个项目的foreach稍慢。
Cast方法将遍历列表并转换每个项目。
如果您要使用该列表数千次,只需将转换结果存储为列表。
如果无法做到(即每次更改列表),请考虑使用 List&lt; IB&gt;
而不是 List&lt; A&gt;
。
这不是C#中的协方差吗?我没有看到你想要做什么,所以我不能评论为什么它会成千上万次。
仅仅对两种方法进行基准测试(Cast扩展方法和带有强制转换的for循环)是一件相当简单的事情。但鉴于Cast是Enumerable类的扩展方法并且通常与IEnumerables进行交易,我想这正是它的实现。如果你想要最快的速度,最好实现你自己的扩展方法,该方法专门用于List(通过索引获取每个元素),考虑到迭代器的开销,这应该稍快一些。但是,这两种方法都需要花费O(n)时间,因此差异不应该很大。尽管如此,这仍然值得进行基准测试......