假设我有一个从基类继承的对象集合.就像是...
abstract public class Animal { } public class Dog :Animal { } class Monkey : Animal { }
现在,我们需要喂养这些动物,但不允许他们知道如何喂养自己.如果他们可以,答案是直截了当的:
foreach( Animal a in myAnimals ) { a.feed(); }
但是,他们不知道如何养活自己,所以我们想做这样的事情:
class Program { static void Main(string[] args) { ListmyAnimals = new List (); myAnimals.Add(new Monkey()); myAnimals.Add(new Dog()); foreach (Animal a in myAnimals) { Program.FeedAnimal(a); } } void FeedAnimal(Monkey m) { Console.WriteLine("Fed a monkey."); } void FeedAnimal(Dog d) { Console.WriteLine("Fed a dog."); } }
当然,这不会编译,因为它会迫使垂头丧气.
感觉好像有一个设计模式或其他一些带有泛型的解决方案可以帮助我摆脱这个问题,但我还没有把手指放在它上面.
建议?
在Linq习语中使用的检查向下转换非常安全.
foreach (Monkey m in myAnimals.OfType()) Program.FeedAnimal(m);
或者你可以使用访客模式.访问者对象知道所有类型的动物,因此它具有FeedAnimal
每个动物的功能.您将访问者对象传递给动物的Feed
函数,并回调正确的FeedAnimal
方法,传递this
.
为了使其可扩展,您需要一个Dictionary
动物饲养者:
private static Dictionary> _feeders;
要注册一个喂食动作,你可以这样做:
_feeders[typeof(Monkey)] = a => { Monkey m = (Monkey)a; // give food to m somehow };
但是有一个向下倾斜,你必须在键中给出正确的类型.所以做一个帮手:
public static void AddFeeder(Action feeder) where TAnimal : Animal { _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a); }
这将捕获模式,以便您可以完全安全地重用它:
AddFeeder(monkey => GiveBananasTo(monkey)); AddFeeder (dog => ThrowBiscuitsAt(dog));
然后,当您需要喂养动物时,请使用此扩展方法:
public static void Feed(this Animal a) { _feeders[a.GetType()](a); }
例如
a.Feed();
所以_feeders将是与扩展方法相同的静态类中的私有静态字段,以及AddFeeder
方法.
更新:一个地方的所有代码,也支持继承:
public static class AnimalFeeding { private static Dictionary> _feeders = new Dictionary >(); public static void AddFeeder (Action feeder) where TAnimal : Animal { _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a); } public static void Feed(this Animal a) { for (Type t = a.GetType(); t != null; t = t.BaseType) { Action feeder; if (_feeders.TryGetValue(t, out feeder)) { feeder(a); return; } } throw new SystemException("No feeder found for " + a.GetType()); } }
您也可以通过各种类型的接口支持循环t
- 基本上相同的想法,所以我在这里保持示例简单.