使用LINQ To Objects创建键/值集合

时间:2009-09-10 13:53:10

标签: linq collections linq-to-objects

我正在尝试使用LINQ To Objects来创建一个查询,该查询将为我提供文件,按文件名索引,并将值映射到二进制数据byte[]

然而,我找不到一个'整洁'的方法来做到这一点。我希望得到类似Dictionary<T,K>输出的内容。

这是我到目前为止所拥有的。示例delimFileNames =“1.jpg | 2.jpg”

//Extract filenames from filename string
//and read file binary from file
//select result into a filename indexed collection
var result = from f in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
            let filePath = Path.Combine(ddsClient.WorkingDirectory, f)
            let fileData = File.ReadAllBytes(filePath)
            select new KeyValuePair<string, byte[]>(f, fileData);

return result.ToDictionary(kvp => kvp.Key, kvp=> kvp.Value);

主要的搔痒是为什么我不能使用无参数ToDictionary()或直接演员。任何改善上述内容的建议或替代方案均值得赞赏。

3 个答案:

答案 0 :(得分:5)

ToDictionary()必须有一个KeyValuePair的特例 - 您当然可以添加为自己的扩展方法:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
     IEnumerable<KeyValuePair<TKey, TValue>> source)
{
    return source.ToDictionary(kvp => kvp.Key, kvp=> kvp.Value);
}

如果没有这种扩展方法,你可以这样做:

var result = from f in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
                                     .ToDictionary(f => f,
          // Outdented to avoid scrolling
          f => File.ReadAllBytes(Path.Combine(ddsClient.WorkingDirectory, f));

基本上,查询表达式允许您执行所有时髦的“let”操作,但强制您在末尾有select,这会使您传播两个不同的值(键和字节数组)分为两个属性。

答案 1 :(得分:2)

  

“主要问题是为什么我不能使用无参数的ToDictionary()[...]”

因为ToDictionary方法需要知道什么是密钥以及值是什么。微软可能已经为这个特殊情况创造了一个过载,但没有,因为我想这个好处不是很大。但是没有什么可以阻止你创建自己的扩展方法,而这正好就是这样。

  

“[...]或直接演员。”

因为您的Linq查询返回的IEnumerable<KeyValuePair<TKey, TValue>>不是Dictionary<TKey, TValue>。它允许您枚举键/值对,但不允许按键快速查找。

请注意,Dictionary<TKey, TValue>IEnumerable<KeyValuePair<TKey, TValue>>虽然(它实现了该接口),这意味着您可以像使用Linq查询一样迭代键/值对,但是底层type是一个Dictionary,因此您可以通过键快速查找。

答案 2 :(得分:1)

您不需要使用KeyValuePair<,>,而是可以使用匿名类型,允许您使用比KeyValue更具描述性的名称:

var files =
    from fileName in delimFileNames.Split(Constants.DDS_FILENAME_SEPARATOR)
    let filePath = Path.Combine(ddsClient.WorkingDirectory, fileName)
    select new
    {
        Path = filePath,
        Data = File.ReadAllBytes(filePath)
    };

return files.ToDictionary(file => file.Path, file => file.Data);