我正在尝试将项目添加到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。
答案 0 :(得分:2)
嗯,你应该测量它们以确定,但只是通过查看我会怀疑方法2更快,原因如下:
Concat
将枚举原始字典,这是不必要的ToDictionary
将通过遍历两个对象中的连接项来创建 new 字典。方法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);