题
是否有更好的方法将基类列表绑定到除了向下转换之外的UI,例如:
static void Main(string[] args) {
List<Animal> list = new List<Animal>();
Pig p = new Pig(5);
Dog d = new Dog("/images/dog1.jpg");
list.Add(p);
list.Add(d);
foreach (Animal a in list)
{
DoPigStuff(a as Pig);
DoDogStuff(a as Dog);
}
}
static void DoPigStuff(Pig p)
{
if (p != null)
{
label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
}
}
static void DoDogStuff(Dog d) {
if (d != null)
{
Image1.src = d.Image;
}
}
class Animal {
public String Name { get; set; }
}
class Pig : Animal{
public int TailLength { get; set; }
public Pig(int tailLength)
{
Name = "Mr Pig";
TailLength = tailLength;
}
}
class Dog : Animal {
public String Image { get; set; }
public Dog(String image)
{
Name = "Mr Dog";
Image = image;
}
}
解决方案
当遇到此类问题时,我会遵循访客模式。
interface IVisitor
{
void DoPigStuff(Piggy p);
void DoDogStuff(Doggy d);
}
class GuiVisitor : IVisitor
{
void DoPigStuff(Piggy p)
{
label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
}
void DoDogStuff(Doggy d)
{
Image1.src = d.Image;
}
}
abstract class Animal
{
public String Name { get; set; }
public abstract void Visit(IVisitor visitor);
}
class Piggy : Animal
{
public int TailLength { get; set; }
public Piggy(int tailLength)
{
Name = "Mr Pig";
TailLength = tailLength;
}
public void Visit(IVisitor visitor)
{
visitor.DoPigStuff(this);
}
}
class Doggy : Animal
{
public String Image { get; set; }
public Doggy(String image)
{
Name = "Mr Dog";
Image = image;
}
public void Visit(IVisitor visitor)
{
visitor.DoDogStuff(this);
}
}
public class AnimalProgram
{
static void Main(string[] args) {
List<Animal> list = new List<Animal>();
Pig p = new Pig(5);
Dog d = new Dog("/images/dog1.jpg");
list.Add(p);
list.Add(d);
IVisitor visitor = new GuiVisitor();
foreach (Animal a in list)
{
a.Visit(visitor);
}
}
}
因此,访问者模式模拟了传统的单调度面向对象语言(如Java,Smalltalk,C#和C ++)中的双重调度。
此代码优于 jop '的唯一优势s,当您需要添加新类型的访问者时(例如 XmlSerializeVisitor 或 FeedAnimalVisitor ),可以在不同的类上实现IVisitor界面。
其他提示
为什么不让Animal包含Pig和Dog被迫实施的抽象方法
public class Animal
{
public abstract void DoStuff();
}
public Dog : Animal
{
public override void DoStuff()
{
// Do dog specific stuff here
}
}
public Pig : Animal
{
public override void DoStuff()
{
// Do pig specific stuff here
}
}
这样,每个特定的类都对其操作负责,使您的代码更简单。你也不需要在你的foreach循环中进行转换。
另一种方法是在调用方法之前执行类型检查:
if (animal is Pig) DoPigStuff();
if (animal is Dog) DoDogStuff();
您正在寻找的是多次发送。 NO - C#不支持多次发送。它只支持单一发送。 C#只能根据接收者的类型动态调用一个方法(即方法调用中。的左侧对象)
此代码使用双重发送。我会让代码说明一切:
class DoubleDispatchSample
{
static void Main(string[]args)
{
List<Animal> list = new List<Animal>();
Pig p = new Pig(5);
Dog d = new Dog(@"/images/dog1.jpg");
list.Add(p);
list.Add(d);
Binder binder = new Binder(); // the class that knows how databinding works
foreach (Animal a in list)
{
a.BindoTo(binder); // initiate the binding
}
}
}
class Binder
{
public void DoPigStuff(Pig p)
{
label1.Text = String.Format("The pigs tail is {0}", p.TailLength);
}
public void DoDogStuff(Dog d)
{
Image1.src = d.Image;
}
}
internal abstract class Animal
{
public String Name
{
get;
set;
}
protected abstract void BindTo(Binder binder);
}
internal class Pig : Animal
{
public int TailLength
{
get;
set;
}
public Pig(int tailLength)
{
Name = "Mr Pig";
TailLength = tailLength;
}
protected override void BindTo(Binder binder)
{
// Pig knows that it's a pig - so call the appropriate method.
binder.DoPigStuff(this);
}
}
internal class Dog : Animal
{
public String Image
{
get;
set;
}
public Dog(String image)
{
Name = "Mr Dog";
Image = image;
}
protected override void BindTo(Binder binder)
{
// Pig knows that it's a pig - so call the appropriate method.
binder.DoDogStuff(this);
}
}
注意:您的示例代码比这简单得多。我认为双重派遣是C#编程中的重要手段之一 - 我只把它作为最后的手段。但是如果有很多类型的对象和许多不同类型的绑定需要做(例如,你需要将它绑定到HTML页面,但你还需要将它绑定到WinForms或报表或CSV) ,我最终会重构我的代码以使用双重调度。
您没有充分利用您的基类。如果你的Animal类中有一个虚拟函数,那么Dog&amp;猪超越,你不需要施放任何东西。
除非你有更具体的例子,否则只需覆盖ToString()。
我认为你想要一个与工厂相关的视图类。
Dictionary<Func<Animal, bool>, Func<Animal, AnimalView>> factories;
factories.Add(item => item is Dog, item => new DogView(item as Dog));
factories.Add(item => item is Pig, item => new PigView(item as Pig));
然后你的DogView和PigView将继承看起来像:
的AnimalViewclass AnimalView {
abstract void DoStuff();
}
你最终会做类似的事情:
foreach (animal in list)
foreach (entry in factories)
if (entry.Key(animal)) entry.Value(animal).DoStuff();
我猜你也可以说这是战略模式的实现。
不隶属于 StackOverflow