Efficiently Obtain IReadOnlyDictionary<int, Animals> from Dictionary<int, Fleas>

荒凉一梦 提交于 2019-12-10 11:22:18

问题


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 Is there a more efficient way to obtain Animals from fleas?


回答1:


.NET supports covariance in interfaces, delegates, generic types and arrays. The interface or type has to specify it's covariant though with the out keyword.

You can write

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

or

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

This works because Dictionary.Values returns an IEnumerable<Flea> and IEnumerable is covariant - its definition is IEnumerable<out T>.

KeyValuePair though isn't covariant which means that the classes that use it like IDictionary<TKey,TValue> and IReadOnlyDictionary<TKey,TValue> aren't either. This was intentional.

Since you only need to read from that dictionary, you can create an accessor method using a delegate or, in C# 7 and later, a local function. You can pass that function to methods that expect a Func<TKey,TValue> and use it to read values from the dictionary.

If you have a method that needs key-based access, let's say :

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

In C# 7 you can write :

var dict =...

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

Process(get);

This cheats a bit, by using variable capture to access the dictionary.

Before C# 7 you'd use a delegate :

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

This may seem strange, but that's how LINQ itself works, by using predicates and delegates instead of interfaces and wrappers.




回答2:


The .NET framework does not contain a dictionary wrapper that supports upcasting, but implementing one is trivial:

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;
}

Usage example:

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


来源:https://stackoverflow.com/questions/56593508/efficiently-obtain-ireadonlydictionaryint-animals-from-dictionaryint-fleas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!