Эффективно получить IReadOnlyDictionary <int, Animals> из словаря <int, Fleas> - PullRequest
2 голосов
/ 14 июня 2019
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 ]

2 голосов
/ 14 июня 2019

.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 голос
/ 14 июня 2019

.NET Framework не содержит обертку словаря, которая поддерживает апкастинг, но ее реализация тривиальна:

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);
...