关于c#:使用Linq对象,如何创建< string,string>的空字典

Using Linq to objects, how to create an empty dictionary of <string, string> easily?

要创建空序列,请使用以下命令

1
var empty = Enumerable.Empty<string> ();

是否有一个等价物可以如此容易地创建一个空字典?


不,没有等价物…

Enumerable.Empty()的目的是返回空数组的"缓存"实例。因此,您可以避免创建新数组(return new T[0];的开销)。

您不能将其转换为非只读结构,如IDictionaryDictionary,因为返回的实例可能在以后进行修改,因此会使目的无效…


new Dictionary()出了什么问题?


我假设(至少5年后的现在)空字典实际上意味着空的只读字典。这个结构和空的可枚举序列一样有用。例如,您可能有一个配置类型,该配置类型有一个字典属性(想想json),一旦配置了该属性,就无法对其进行修改:

1
2
3
4
public class MyConfiguration
{
    public IReadOnlyDictionary<string, string> MyProperty { get; set; }
}

但是,如果从未配置该属性呢?那么,MyProperty就是null。避免意外的NullReferenceException的一个好办法是用空字典初始化属性:

1
2
3
4
5
public class MyConfiguration
{
    public IReadOnlyDictionary<string, string> MyProperty { get; set; }
        = new Dictionary<string, string>();
}

缺点是,每个MyConfiguration的分配都需要一个空字典的分配。为了避免这种情况,您需要类似于Enumerable.Empty()的东西,即缓存的空只读字典。

实现这一点有两种方法。第一种方法是依赖System.Collections.Immutable。ImmutableDictionary实现IReadOnlyDictionary,它有一个Empty字段,您可以使用:

1
IReadOnlyDictionary<string, string> empty = ImmutableDictionary<string, string>.Empty;

或者,您可以实现自己的空只读字典,类似于Enumerable.Empty()Array.Empty()。请注意,空值不再是字段,类也不是泛型。相反,它是一个通用方法。这需要两个类。

第一个类是"隐藏"的,可以是内部类:

1
2
3
4
5
internal static class EmptyReadOnlyDictionary<TKey, TValue>
{
    public static readonly IReadOnlyDictionary<TKey, TValue> Instance
        = new Dictionary<TKey, TValue>();
}

第二个类使用第一个类,但将其隐藏在IReadOnlyDictionary接口后面:

1
2
3
4
5
public static class ReadOnlyDictionary
{
    public static IReadOnlyDictionary<TKey, TValue> Empty<TKey, TValue>()
        => EmptyReadOnlyDictionary<TKey, TValue>.Instance;
}

用途:

1
IReadOnlyDictionary<string, string> empty = ReadOnlyDictionary.Empty<string, string>();

对于这两种解决方案,对于TKeyTValue的每个不同组合,都只有一个空字典实例。


回到2019年,有一种方法可以做到这一点,使用:

1
ImmutableDictionary<TKey, TValue>.Empty

更多信息可以在这里找到(最后几篇文章):https://github.com/dotnet/corefx/issues/25023


当键和值具有相同类型(例如:字符串)时:

1
Enumerable.Empty<string>().ToDictionary(x=>x, x=>x)


1
Enumerable.Empty<KeyValuePair<string, object>>().ToDictionary(kvp => kvp.Key, kvp => kvp.Value)