C#:如何使用IReadOnly字典实现接口,该字典包含来自包含具体值的具体字典的接口值



在我的代码中,我声明了内部类和公共接口,我遇到了这样的情况:我想用包含接口类型值的IReadonlyDictionary来公开接口,但我想用具有具体类型值的Dictionary的类来实现它。

我需要这个,因为我正在将一些JSON反序列化为具体类型,并且我希望将反序列化的数据公开为只读。

我已经实现了它,如下所示,通过懒洋洋地创建一个新的字典,其中具体的值被转换到接口值中。有没有一种方法可以做到这一点而不必创建一本新词典?

// In this code I want to expose an IReadonlyDictionary<string,ISomeValue>. 
// The data is deserialized from JSON by using concrete type with a dictionary 
// with values of type SomeValue which has to have public getter/setters to be deserialized.
// I would like to do this without copying and casting into a new dictionary as shown here.
// Is that possible?

public interface ISomeValue { }
internal class SomeValue : ISomeValue { }

public interface IConfiguration {
IReadOnlyDictionary<string, ISomeValue> Values{ get; }
}

internal class Configuration : IConfiguration  {
public Configuration() {
_values = new Lazy<IReadOnlyDictionary<string, ISomeValue>>(()
=> Values.ToDictionary(x=>x.Key,x=>(ISomeValue)x.Value));
}
public Dictionary<string, SomeValue> Values { get; } = null!;
private Lazy<IReadOnlyDictionary<string, ISomeValue>> _values;
IReadOnlyDictionary<string, ISomeValue> IConfiguration.Values=> _values.Value;
}

您必须创建一个新对象,但不必创建原始字典的副本。相反,您可以使用新形状在原始字典上创建一个视图。以下是一些完全未经测试但简单的代码,我认为应该有效:

public class ReadOnlyDictionaryWrapper<TKey, TValue, TOriginalValue> : IReadOnlyDictionary<TKey, TValue>
where TOriginalValue : TValue
{
private readonly IReadOnlyDictionary<TKey, TOriginalValue> original;
public ReadOnlyDictionaryWrapper(IReadOnlyDictionary<TKey, TOriginalValue> original) =>
this.original = original;
public TValue this[TKey key] => original[key];
public IEnumerable<TKey> Keys => original.Keys;
public IEnumerable<TValue> Values => original.Values.Cast<TValue>();
public int Count => original.Count;
public bool ContainsKey(TKey key) => original.ContainsKey(key);
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() =>
original.Select(pair => new KeyValuePair<TKey, TValue>(pair.Key, pair.Value))
.GetEnumerator();
public bool TryGetValue(TKey key, out TValue value)
{
bool ret = original.TryGetValue(key, out var originalValue);
value = originalValue;
return ret;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

我建议在Configuration构造函数中创建一个这样的实例来包装_values

我想在不复制和转换到新字典的情况下完成这项工作,如图所示。

在显式接口实现中,不能直接返回Value,因为IReadOnlyDictionary的两个泛型参数都是泛型不变的。如果只有TValue是协变的,你就可以直接这样做:

public Dictionary<string, SomeValue> Values { get; } = new Dictionary<string, SomeValue>();
IReadOnlyDictionary<string, ISomeValue> IConfiguration.Values => Values;

TryGetValue使用TValue作为参数,IReadOnlyDictionary被迫在TValue上保持不变。

我建议您在其他方面让代码看起来更漂亮。例如,您可以将创建一个新字典的一行很长的代码提取到扩展方法中。

最新更新