问题
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