Foreach vs IEnumerable.Concat()效率

时间:2014-08-11 19:50:26

标签: c# linq

我正在尝试将项目添加到assemblyPaths集合中每个项目的字典中。

我的问题是哪个是添加到结果字典中的最快方法?方法1或方法2(见下面的代码)。对于方法1,我使用Concat将新创建的Dictionary与主要的Dictionary(pluginTypes)合并。对于方法2,我使用一个简单的foreach添加到每个子字典的主字典。这两种方法的性能确实存在差异吗?

Dictionary<Type, string> pluginTypes = new Dictionary<Type, string>();

foreach (string assemblyPath in assemblyPaths)
{

Assembly assembly = RuntimeContext.Current.LoadAssembly(assemblyPath);

var plugins = (from type in assembly.GetTypes()
                let attribs = type.GetCustomAttributes(true)
                where attribs.Select(x => (x as Attribute).GetType().FullName).Contains("PluginAttribute")
                select type)
                .ToDictionary(k => k, v => assemblyPath);

  // METHOD 1
  pluginTypes = pluginTypes
  .Concat(plugins)
  .ToDictionary(k => k.Key, v => v.Value);

  // METHOD 2
  foreach (Type type in plugins.Keys)
     pluginTypes.Add(type, plugins[type]);

}

就个人而言,我更喜欢方法1。

5 个答案:

答案 0 :(得分:2)

嗯,你应该测量它们以确定,但只是通过查看我会怀疑方法2更快,原因如下:

  1. Concat将枚举原始字典,这是不必要的
  2. ToDictionary将通过遍历两个对象中的连接项来创建 new 字典。
  3. 方法2更清洁,使用更少的内存,可能更快。

    作为旁注,您不需要在查询中创建中间字典;您可以在添加到字典时自行引用查询结果:

    var plugins = (from type in assembly.GetTypes()
                    let attribs = type.GetCustomAttributes(true)
                    where attribs.Select(x => (x as Attribute).GetType().FullName).Contains("PluginAttribute")
                    select type)
    
      foreach (Type type in plugins)
         pluginTypes.Add(type, assemblyPath);
    

答案 1 :(得分:2)

如果没有实际测试这两种方法(当然,总是最好的方法),可以尝试逻辑地攻击方法。

方法2采用现有集合,并向其添加一些新项目。

方法1从两个不同的集合中提取所有元素,并从中构建一个全新的集合。

在我看来,方法1的工作量大约是第一次迭代的3倍。在那之后,它变成了工作量的四倍,然后是你从pluginTypes中提取更多东西并且用它们构建一个新词典的5倍。

事实上,为什么要将plugins构建为字典?你只是线性地拉出物品....

你真正想要的是:

 Assembly assembly = RuntimeContext.Current.LoadAssembly(assemblyPath);
 foreach (Type type in assembly.GetTypes())
 {
   if (type.GetCustomAttributes(true)
           .Select(x => (x as Attribute).GetType().FullName).Contains("PluginAttribute"))
      pluginTypes.Add(type, assemblyPath);
 }

另外,我们需要问一下,我们在客户属性中究竟在寻找什么?一个名为&#34; PluginAttribute&#34;?的属性包含&#34; PluginAttribute&#34;的属性用它的名字(例如&#34; WidgetPluginAttribute&#34;)? PluginAttribute?中的属性派生。我的猜测是最后一个可能是最接近的,所以让我们一起去。

 foreach (Type type in assembly.GetTypes())
 {
   if (type.GetCustomAttributes(true)
            .Any(ca=> typeof(PluginAttribute).IsAssignablefrom(ca.GetType())))
      pluginTypes.Add(type, assemblyPath);
 }

答案 2 :(得分:2)

在考虑表现之前,请考虑以下事项:

  • 您正在创建一个Dictionary<Type, string>,其中可以从密钥本身访问该值(使用Type.Assembly.Location),因此使用字典没有意义。
  • 如果您定义了课程PluginAttribute,则可以对此进行比较

E.g。这段代码:

private ISet<Type> GetTypes(IEnumerable<string> assemblyPaths)
{
    return new HashSet<Type>(assemblyPaths
        .Select(RuntimeContext.Current.LoadAssembly)
        .SelectMany(a => a.GetTypes())
        .Where(t => t.GetCustomAttribute<PluginTypeAttribute>() != null));
}

或者使用IList,具体取决于您是否要以字典方式进行迭代或使用它。

答案 3 :(得分:1)

方法2通常应该更快。 ToDictionary基本上会始终重新创建字典,Concat会返回IEnumerable。为什么要从现有的词典再到IEnumerable重新创建字典呢?

如果您没有唯一的密钥,请小心,在这两种情况下都可能会出现异常。

答案 4 :(得分:0)

LINQ操作专门接受一系列项目,将每个项目投影到一个新序列中,然后返回一个表示所有连接在一起的投影序列的序列。该操作是SelectMany。您在每个查询中使用Concat的变体在语义上是相同的,但会导致许多额外的间接级别,以及大量不必要的开销。

除此之外,每次迭代外循环时,都会不断创建并丢弃字典。由于字典的构建耗费时间和内存,因此会产生显着的负面性能影响。

你的第二个解决方案确实消除了一些不必要的开销,它通过改变对象而不是通过使用查询操作来实现。这并不意味着你不能使用查询操作来有效地解决这个问题,你只需要使用正确的问题。

Dictionary<Type, string> pluginTypes =
    (from assemblyPath in assemblyPaths
        let assembly = RuntimeContext.Current.LoadAssembly(assemblyPath)
        from type in assembly.GetTypes()
        let attribs = type.GetCustomAttributes(true)
        where attribs.Select(x => (x as Attribute).GetType().FullName)
        .Contains("PluginAttribute")
        select new
        {
            key = type,
            value = assemblyPath
        })
    .ToDictionary(pair => pair.key, pair => pair.value);