Помощь по Linq и методу словаря ContainsKey - PullRequest
0 голосов
/ 18 августа 2010

Я пишу инструмент, и первая часть этого инструмента - собрать все файлы заголовков в нашем общедоступном API. Проблема в том, что два заголовочных файла имеют повторяющиеся имена (но они находятся в разных папках). Это вызовет проблемы при создании словаря.

Первоначально я написал цикл foreach для сбора экземпляров FileInfo в словарь. Однако в последнее время я изучаю LINQ, и я хотел преобразовать цикл foreach в оператор LINQ. Проблема в том, что когда он выполнялся, он жаловался на дублирующееся имя файла.

Вот оригинальный код:

public Dictionary<String, FileDependency> GetSDKFiles(DirectoryInfo dir)
{
    Dictionary<String, FileDependency> list = new Dictionary<String, FileDependency>();
    foreach (FileInfo info in dir.EnumerateFiles("*.h", SearchOption.AllDirectories))
    {
        String key = info.Name.ToLower();
        if (list.ContainsKey(key) == false)
        {
            list.Add(key, new FileDependency(info.FullName));
        }
        else
        {
            Debug.Print("Duplicate key: {0}", info.Name);
            Debug.Print("  File: {0}", info.FullName);
            Debug.Print("  Have: {0}", list[key].FullFileName);
        }
    }

    return list;
}

Который я пытался превратить в LINQ так:

public Dictionary<String, FileDependency> GetSDKFilesLINQ(DirectoryInfo dir)
{
    var files = from info in dir.EnumerateFiles("*.h", SearchOption.AllDirectories)
                let key = info.Name.ToLower()
                let dep = new FileDependency(info.FullName)
                select new { key, dep };
    return files.ToDictionary(v => v.key, v => v.dep);
}

Однако во время выполнения я получаю это:

Элемент с таким же ключом уже добавлен.

В цикле foreach этого было легко избежать, поскольку я вызвал метод ContainsKey, чтобы убедиться, что у меня нет дубликатов. Но что такое эквивалент LINQ?

Где я использую? - Как? Я использую группу? - Как?

Спасибо.

Ответы [ 2 ]

5 голосов
/ 18 августа 2010
var files = dir.EnumerateFiles("*.h", SearchOption.AllDirectories)
               .GroupBy(file => file.Name.ToLower())
               .Select(group => new {Key = group.Key, Value = group.First()})
               .ToDictionary(a => a.Key, a => new FileDependency (a.Value.FullName));

Если у вас есть MoreLinq , вы можете сделать:

var files =  dir.EnumerateFiles("*.h", SearchOption.AllDirectories)
                .DistinctBy(file => file.Name.ToLower())
                .ToDictionary(file => new FileDependency (a.Value.FullName));

В качестве альтернативы, вы можете написать свою собственную реализацию IEqualityComparer для файлов и использовать стандарт Distinct метод.Вся проблема здесь в том, что Distinct (по крайней мере, начиная с .NET 3.5) не поставляется с перегрузкой, которая позволяет вставлять ваше собственное определение «отличимости» как лямбда-выражения.

1 голос
/ 18 августа 2010

Вы можете сгруппировать по ключу и взять первое значение из группы для dep:

public Dictionary<String, FileDependency> GetSDKFilesLINQ(DirectoryInfo dir)
{
    var files = from info in dir.EnumerateFiles(
                    "*.h", SearchOption.AllDirectories)
                let key = info.Name.ToLower()
                let dep = new FileDependency(info.FullName)
                group dep by key into g
                select new { key = g.Key, dep = g.First() };
    return files.ToDictionary(v => v.key, v => v.dep);
}

Это будет игнорировать дубликаты. В качестве альтернативы, вы можете использовать Lookup вместо словаря:

public ILookup<String, FileDependency> GetSDKFilesLINQ2(DirectoryInfo dir)
{
    var files = from info in dir.EnumerateFiles(
                    "*.h", SearchOption.AllDirectories)
                let key = info.Name.ToLower()
                let dep = new FileDependency(info.FullName)
                select new { key, dep };
    return files.ToLookup(v => v.key, v => v.dep);
}

Индексатор при поиске вернет IEnumerable<FileDependency>, так что вы сможете увидеть все значения.

...