如何将字典转换为ConcurrentDictionary?

14

我已经看到如何将ConcurrentDictionary转换为Dictionary,但我有一个字典并想将其转换为ConcurrentDictionary。我该怎么做?更好的是,我能否将链接语句设置为ConcurrentDictionary?

var customers = _customerRepo.Query().Select().ToDictionary(x => x.id, x => x);

你使用的是哪个LINQ提供程序?Query()Select()的结果是IQueryable还是IEnumerable? - Panagiotis Kanavos
4个回答

30

你可以使用LINQ将结果投影到字典中,但是你需要编写自己的ToConcurrentDictionary扩展方法来完成它。(你可能可以通过使用foreach并采用键和值选择器委托来消除中间字典,并直接插入)。 - Scott Chamberlain
你可以使用LINQ和你提到的构造函数。从原始可枚举对象中创建一个带有所需键和值的KeyValuePair,然后将其传递给构造函数,例如:myEnumerable= ... Select(item=>new KeyValuePair(item.Id,item); ... new ConcurrentDictionary(myEnumerable);。不需要ForEach,尽管扩展方法可以使代码稍微更清晰一些。 - Panagiotis Kanavos

7
为什么不自己编写扩展方法呢:
  public static class ConcurrentDictionaryExtensions {
    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");

        ConcurrentDictionary<TKey, TElement> d = new ConcurrentDictionary<TKey, TElement>(comparer ?? EqualityComparer<TKey>.Default);
        foreach (TSource element in source)
            d.TryAdd(keySelector(element), elementSelector(element));

        return d;
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, null);
    }

    public static ConcurrentDictionary<TKey, TSource> ToConcurrentDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) {
        return ToConcurrentDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, comparer);
    }

    public static ConcurrentDictionary<TKey, TElement> ToConcurrentDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
        return ToConcurrentDictionary<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
    }

    internal class IdentityFunction<TElement> {
        public static Func<TElement, TElement> Instance
        {
            get { return x => x; }
        }
    }

}

这段代码只是从.Net框架中借鉴而来。


不确定为什么,但如果你不传递一个比较器它就会抛出异常。参见来源 - MotKohn
@MotKohn:你说得对。根据source修复了代码。 - Oliver

4
一个LINQ-To-Objects语句最终是一个“IEnumerable”,因此您可以将其传递给ConcurrentDictionary构造函数,例如:
var customers = myCustomers.Select(x => new KeyValuePair(x.id, x));
var dictionary=new ConcurrentDictionary(customers);

这可能不适用于其他提供程序。例如,Linq to Entities将整个LINQ语句转换为SQL,并且无法投影到KeyValuePair。在这种情况下,您可能需要调用AsEnumerable()或任何其他强制执行IQueryable的方法,例如:

var customers = _customerRepo.Customers.Where(...)
                             .AsEnumerable()
                             .Select(x => new KeyValuePair(x.id, x));
var dictionary=new ConcurrentDictionary(customers);

Select()方法如果没有参数,不是IEnumerable或IQueryable方法,因此我认为它是某个其他ORM提供的方法。如果Select()返回一个IEnumerable,则可以使用第一种选项,否则可以使用AsEnumerable()方法。


请注意,您可以在可枚举对象上执行 AsEnumerable() 操作,并不会对其造成任何影响。 - Scott Chamberlain

0

或者只是一个 have 方法:

 private ConcurrentDictionary<TKey, TValue> ToConcurrent<TKey, TValue>(Dictionary<TKey, TValue> dic) {
        return new ConcurrentDictionary<TKey, TValue>(dic);
    }

然后执行:

var customers = _customerRepo.Query().Select().ToDictionary(x => x.id, x => x);
var concurrentDic = ToConcurrent(customers);

就我个人而言,我会选择刚刚更新的扩展方法...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接