문제
다운캐스트하는 것 외에 기본 클래스 목록을 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새로운 유형의 방문자 (예 : xmlserializevisitor 또는 a Feedanimalvisitor).
다른 팁
동물이 돼지와 개가 강제로 구현 해야하는 추상적 인 방법을 포함시키는 이유는 무엇입니까?
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();
당신이 찾고있는 것은 다중 파견입니다.아니요 - 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에도 바인딩해야 합니다. 결국에는 이중 디스패치를 사용하도록 코드를 리팩터링할 것입니다.
당신은 당신의 기본 클래스를 최대한 활용하지 않습니다. 동물 수업에서 개와 돼지가 우선적으로 우선적으로 가상 기능을했을 때 아무것도 캐스팅 할 필요가 없습니다.
보다 구체적인 예가 없다면 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는 다음과 같은 모양의 동물 뷰를 상속합니다.
class AnimalView {
abstract void DoStuff();
}
당신은 다음과 같은 일을하게됩니다.
foreach (animal in list)
foreach (entry in factories)
if (entry.Key(animal)) entry.Value(animal).DoStuff();
또한 이것이 전략 패턴의 구현이라고 말할 수 있다고 생각합니다.