【问题标题】:List<T> Casting algorithms and performance considerationsList<T> 铸造算法和性能注意事项
【发布时间】:2009-03-03 15:44:54
【问题描述】:

我有以下代码

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 循环更快,或者这正是它的作用?

EDIT 主类需要维护一个实际对象的列表。但读者只能通过界面触摸它们。

【问题讨论】:

    标签: c# performance casting generic-list


    【解决方案1】:

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

    【讨论】:

    • 这可能完全改变了问题,但如果 A 需要一个与 A 的主列表完全一致的 IB 内部列表怎么办?
    • @devinb:我不太确定你的意思。你的意思是作为一个成员变量?您必须使 A 通用;那么你可以使用相同的技巧。如果这还不够信息,我建议您针对这一点提出一个新问题,提供更多详细信息。
    【解决方案2】:

    为什么不像这样声明列表:

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

    【讨论】:

    • 可以,因为主类需要维护一个实际对象的列表,而读者类只允许访问接口。
    • 对不起,如果我有点慢。但是您是说主类(在我的实际情况下为 EnvironmentClass)应该实现 IIBCollection 我只是不确定 IIBCollection 接口应该应用于什么。
    • 您将拥有诸如 Environment、EnvironmentCollection、IEnvironment 和 IEnvironmentCollection 之类的内容。 Go() 方法将存在于 Collection 中。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      Cast 方法只会循环遍历列表并转换每个项目。

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

      如果这不可能(即您每次都更改列表),请考虑使用List&lt;IB&gt; 而不是List&lt;A&gt;

      【讨论】:

        【解决方案5】:

        这不就是 C# 中的协方差的用途吗?我看不到您要做什么,所以我无法评论为什么它被执行了数千次。

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            猜你喜欢
            • 2018-12-24
            • 2012-02-03
            • 2011-07-09
            • 2017-01-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多