列出<t>投射算法和性能注意事项</t>

时间:2009-03-03 15:44:55

标签: c# performance casting generic-list

我有以下代码

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);
    }

}

我最初尝试传递一个列表但doesn't work。 经过SO的大量帮助后,我发现传递IEnumerable是获取对象的唯一方法.ofType(IB)。

不幸的是,在我的代码中,以下行将被执行数千次:

aList.Cast<IB>();

我想知道是否有人知道它是如何通过算法实现的(在IL中)以及它的时间顺序是什么。

也就是说,它是否比仅仅投射每个项目的foreach循环更快,还是正是它所做的那样?

编辑主类需要维护实际对象的列表。但是只允许读者通过界面触摸它们。

6 个答案:

答案 0 :(得分:10)

您应该将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

答案 1 :(得分:5)

为什么不直接声明列表:

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; 
}}

答案 2 :(得分:1)

它在内部实现为CastIterator,它比转换每个项目的foreach稍慢。

答案 3 :(得分:0)

Cast方法将遍历列表并转换每个项目。

如果您要使用该列表数千次,只需将转换结果存储为列表。

如果无法做到(即您每次都更改列表),请考虑使用List<IB>代替List<A>

答案 4 :(得分:0)

这不是C#中的协方差吗?我没有看到你想要做什么,所以我不能评论为什么它会成千上万次。

答案 5 :(得分:0)

仅仅对两种方法进行基准测试(Cast扩展方法和带有强制转换的for循环)是一件相当简单的事情。但鉴于Cast是Enumerable类的扩展方法并且通常与IEnumerables进行交易,我想这正是它的实现。如果你想要最快的速度,最好实现你自己的扩展方法,该方法专门用于List(通过索引获取每个元素),考虑到迭代器的开销,这应该稍快一些。但是,这两种方法都需要花费O(n)时间,因此差异不应该很大。尽管如此,这仍然值得进行基准测试......