C#中IImmutableDictionary属性初始化异常问题咨询
问题场景与解答
问题重现
以下代码中,使用对象初始化器的集合语法为IImmutableDictionary属性添加元素后,属性实际仍为空,但编译器未报错:
示例类
public class TestClass { public IDictionary<string, string> DictProp { get; set; } = new Dictionary<string, string>(); public IImmutableDictionary<string, string> ImmutDictProp { get; set; } = ImmutableSortedDictionary<string, string>.Empty; }
演示代码
var bad = new TestClass { // 此代码正常工作 DictProp = { {"uno", "one"}, {"due", "two"} }, // 此代码并未初始化字段,但编译器无报错 ImmutDictProp = { { "uno", "one" }, { "due", "two" } } }; Trace.Assert(bad.DictProp.Count == 2); Trace.Assert(bad.ImmutDictProp.Count == 0); // <<<< 预期为2个元素 var good = new TestClass { ImmutDictProp = new Dictionary<string, string> { { "uno", "one" }, { "due", "two" } }.ToImmutableSortedDictionary() }; Trace.Assert(good.ImmutDictProp.Count == 2);
疑问解答
1. 编译器为何允许这种情况发生?
ImmutDictProp = { ... }是对象初始化器中的集合初始化语法,其执行逻辑是:
- 获取属性当前的实例(即类初始化时赋值的
ImmutableSortedDictionary.Empty) - 对该实例依次调用
Add方法传入集合元素
但IImmutableDictionary的Add方法是不可变设计:它不会修改原字典实例,而是返回一个包含新元素的全新不可变字典。而集合初始化语法仅会调用Add方法,不会将返回的新实例重新赋值给属性,因此原属性始终指向初始的空字典。
编译器允许该写法的原因是:它仅做语法合法性校验——只要目标类型存在匹配签名的Add方法(IImmutableDictionary确实存在),就判定语法合法,不会校验Add方法是否会修改实例本身。
2. 是否应该避免此类字典初始化方式?
必须避免。这种写法极具误导性,代码字面看起来是在填充属性元素,但实际不会产生任何预期效果,极易引发难以排查的逻辑bug。
推荐的正确写法是直接赋值全新的不可变字典实例,比如:
// 方式1:直接用CreateRange创建 var good = new TestClass { ImmutDictProp = ImmutableSortedDictionary.CreateRange(new[] { new KeyValuePair<string, string>("uno", "one"), new KeyValuePair<string, string>("due", "two") }) }; // 方式2:从普通字典转换(如你示例中的写法) var good2 = new TestClass { ImmutDictProp = new Dictionary<string, string> { {"uno", "one"}, {"due", "two"} }.ToImmutableSortedDictionary() };
内容的提问来源于stack exchange,提问作者Peter L
相关产品推荐
相关产品推荐

