假设我有:
public class Animal
{
virtual public void Attack() {};
}
public class Lion : Animal
{
public override void Attack() { base.Attack(); }
}
public class Boar : Animal
{
public override void Attack() { base.Attack(); }
}
以及这两种动物的容器:
Dictionary<int, Lion> lions;
Dictionary<int, Boar> boars;
是否有一种方法可以转换“狮子”和“公猪”,以便我可以将它们传递给这样的函数?
void IterateTable(Dictionary<int, Animal> dictionary)
{
foreach(var entry in dictionary)
entry.Value.Attack();
}
也许是这样?
void IterateTable<T>(Dictionary<int, T> dictionary)
where T : Animal
{
foreach(var entry in dictionary)
entry.Value.Attack();
}
您的代码按书面规定工作。当字典值中的动物调用其Attack()方法时,它将调用适当的动物特定方法。这称为covariance。您可以为字典提供比其签名所要求的更为具体的类型。
您可以按如下所示修改您的代码:
void Main()
{
Dictionary<int, Animal> dictionary = new Dictionary<int, Animal>()
{
[1] = new Lion(),
[2] = new Boar()
};
IterateTable(dictionary);
}
public class Animal
{
virtual public void Attack() { Console.WriteLine("Default animal attack"); }
}
public class Lion : Animal
{
public override void Attack() { Console.WriteLine("Lion attack"); }
}
public class Boar : Animal
{
public override void Attack() { Console.WriteLine("Boar attack"); }
}
void IterateTable(Dictionary<int, Animal> dictionary)
{
foreach (var entry in dictionary)
entry.Value.Attack();
}
输出:
狮子攻击
野猪袭击
问题是,如果您传入字典,则没有什么阻止IterateTable
向其添加元素。那是个问题。由于IterateTable
认为它是Dictionary<Animal>
,因此可以添加“野猪”或“狮子”或两者。这显然是行不通的。
void IterateTable(Dictionary<int,Animal> dictionary)
{
dictionary.Add(1, new Boar()); //This would fail if you'd passed in a dictionary of Lions
dictionary.Add(2, new Lion()); //This would fail if you'd passed in a dictionary of Boars
}
由于IterateTable
实际上不必在字典中添加任何元素,因此您可以更改其签名,以便它接受诸如IEnumerable<T>
之类的只读对象。因为该接口不允许添加任何东西,所以它是合法的,并且编译错误也消失了。
void IterateTable(IEnumerable<Animal> dictionary)
{
foreach (var entry in dictionary)
entry.Attack();
}
现在您可以编写此代码,它将正常编译:
var lions = new Dictionary<int, Lion>();
var boars = new Dictionary<int, Boar>();
IterateTable(lions.Values);
IterateTable(boars.Values);
顺便说一下,这个概念叫做covariance,是理解泛型的一个非常重要的部分。
A Dictionary<TKey,TValue>
是IEnumerable<KeyValuePair<TKey,TValue>>
。
所以,简单的方法是使用`ToDictionary(),只是在值选择器中上载值:
Dictionary<string,Lion> dict = new Dictionary<string,Lion>( GetMeSomeLions() );
Dictionary<string,Animal> dictAnimals = dict.ToDictionary(
kvp => kvp.Key, // Key selector
kvp => (Animal) kvp.Value // Value selector
);
如果从以下位置更改容器:
Dictionary<int, Lion> lions;
到
Dictionary<int, Animal> lions;
不是初学者,那么我将按如下方式打电话:
IterateTable(lions.ToDictionary(
k => k.Key,
v => (Animal)v.Value)
));