查找在 ToDictionary() 中抛出异常的元素的最佳方法

Best method to find element which throws exception in ToDictionary()

我有一个由一些更大的配置文件填充的项目列表。

List<TextEntrtry> localTextEntries;

具有 TextEntry 类型的元素:

 public class TextEntry
 {
    public Guid Id { get;  set; }
    ....

此列表已转换为字典:

Dictionary<Guid, TextEntry> textEntries;

并且此行抛出异常 'Element with same key already exists':

 textEntries = localTextEntries.ToDictionary(x => x.Id);

显然我的列表包含两个具有相同 ID 的元素。

我的问题:找出哪些元素导致异常的最佳方法是什么?

(这将允许我生成有意义的错误消息)

运行 在您的 collection 上执行此操作以获取具有重复条目的条目:

var duplicateEntries = localTextEntries.GroupBy(k = > k.Id)
    .Where(g = > g.Count() > 1)
    .Select(g = > g.Key);

您也可以随时添加扩展方法并从源中获取不同的值

IEnumerable <TextEntry> distinctList = localTextEntries.DistinctBy(x = > x.Id);

public static IEnumerable<TSource> Distinctify<TSource, TKey>(this IEnumerable<TSource> inSrc_, Func<TSource, TKey> keyFunct_)
{
    var uniqueSet = new HashSet<TKey>();
    return inSrc_.Where(tmp => uniqueSet.Add(keyFunct_(tmp)));
}

您可以重写 ToDictionary 以使用您自己的实现,在异常消息中包含 key

//TODO come up with a slightly better name
public static Dictionary<TKey, TValue> MyToDictionary<TSource, TKey, TValue>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TValue> valueSelector,
    IEqualityComparer<TKey> comparer)
{
    comparer = comparer ?? EqualityComparer<TKey>.Default;
    var dictionary = new Dictionary<TKey, TValue>(comparer);
    foreach (var item in source)
    {
        var key = keySelector(item);
        try
        {
            dictionary.Add(key, valueSelector(item));
        }
        catch (ArgumentException ex)
        {
            throw new ArgumentException("Missing key: " + key, ex);
        }
    }
    return dictionary;
}

您想创建没有比较器或值选择器的重载,其中使用这些参数的默认值。

您可能还想创建一种新的 Exception 类型,将密钥存储为 属性,而不是在异常消息中包含密钥的字符串值(如果存在不是对象的良好字符串表示形式)。

您可以使用分组依据来检查重复或不重复的项目。要在分组项目上创建字典 运行 ToDictionary 方法:

// get repeated items
var repeated = localTextEntries.GroupBy(t => t.Id).Where(g => g.Count() > 1).Select(i => i);

// get not repeated items
var notRepeated = localTextEntries.GroupBy(t => t.Id).Where(g => g.Count() == 1).Select(i => i.First());

// find not repeated items and create a dictionary
var dictFromNotRepeated = localTextEntries.GroupBy(t => t.Id)
            .Where(g => g.Count() == 1)
            .ToDictionary(g => g.Key, g => g.First());

最后,@Servy 的方法最适合我。我只是选择了一个匹配的重载并添加了一些更好的错误处理:

public static Dictionary<TKey, TSource> ToDictionary2<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        IEqualityComparer<TKey> comparer = null)
{
    comparer = comparer ?? EqualityComparer<TKey>.Default;
    Dictionary<TKey, TSource> dictionary = 
        new Dictionary<TKey, TSource>(comparer);
    foreach (var item in source)
    {
        var key = keySelector(item);
        try
        {
            dictionary.Add(key, item);
        }
        catch (Exception ex)
        {
            string msg = string.Format("Problems with key {0} value {1}",
                key,
                item);
            throw new Exception(msg, ex);
        }
    }
    return dictionary;
}