有效地从Dictionary <int,Fleas>获取IReadOnlyDictionary <int,Animals>

时间:2019-06-14 07:33:53

标签: c# generics casting covariance contravariance

public class Flea : Animals {...}

var fleas = new Dictionary<int, Flea>();

public IReadOnlyDictionary<string, Animal> Animals => fleas.ToDictionary(pair => pair.Key, pair => (Animal)pair.Value);

Q 是否有更有效的方法从Animals获取fleas

2 个答案:

答案 0 :(得分:2)

.NET支持接口,委托,泛型类型和数组中的协方差。接口或类型必须使用out关键字来指定其协变。

你可以写

IEnumerable<Animal> animals=new List<Flea>();

var dict=new Dictionary<int,Flea>{
    [1]=new Flea()
};
IEnumerable<Animal> animals=dict.Values;

之所以可行,是因为Dictionary.Values返回一个IEnumerable<Flea>并且IEnumerable是协变的-其定义是IEnumerable<out T>

KeyValuePair虽然不是协变的,这意味着使用它的类(例如IDictionary<TKey,TValue>IReadOnlyDictionary<TKey,TValue>也不是)。这是故意的。

由于只需要从该字典中读取,因此可以使用委托或在C#7及更高版本中使用局部函数来创建访问器方法。您可以将该函数传递给期望Func<TKey,TValue>的方法,并使用它从字典中读取值。

如果您有一种需要基于键的访问的方法,那么说:

void Process(Func<int,Animal> reader)
{
    var value=reader(1);
}

在C#7中,您可以编写:

var dict =...

Animal get(int key)=>dict[key];

Process(get);

通过使用变量捕获来访问字典,这有点作弊。

在C#7之前,您将使用一个委托:

Func<int,Animal> get= key=>dict[key];
Process(get);

这似乎很奇怪,但是 LINQ 本身就是这样的,通过使用谓词和委托而不是接口和包装器。

答案 1 :(得分:1)

.NET框架不包含支持向上转换的字典包装,但是实现一个包装很简单:

public class ReadOnlyDictionaryUpcast<TKey, TValueDerived, TValueBase>
    : IReadOnlyDictionary<TKey, TValueBase> where TValueDerived : TValueBase
{
    private readonly Dictionary<TKey, TValueDerived> _dictionary;

    public ReadOnlyDictionaryUpcast(Dictionary<TKey, TValueDerived> dictionary)
    {
        _dictionary = dictionary;
    }

    public int Count => _dictionary.Count;

    public TValueBase this[TKey key] => _dictionary[key];

    public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key);

    public bool TryGetValue(TKey key, out TValueBase value)
    {
        bool result = _dictionary.TryGetValue(key, out TValueDerived valueDerived);
        value = valueDerived;
        return result;
    }

    public IEnumerator<KeyValuePair<TKey, TValueBase>> GetEnumerator() => _dictionary
        .Select(e => new KeyValuePair<TKey, TValueBase>(e.Key, e.Value))
        .GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public IEnumerable<TKey> Keys => _dictionary.Keys;

    public IEnumerable<TValueBase> Values => 
        (IEnumerable<TValueBase>)(IEnumerable<TValueDerived>)_dictionary.Values;
}

用法示例:

var animals = new ReadOnlyDictionaryUpcast<string, Flea, Animal>(fleas);