我有以下课程:
class Alpha
{
}
class Beta : Alpha
{
}
class Gamma : Beta
{
}
class Epsilon : Beta
{
}
我有几种方法可以将它们作为参数:
void Receiver(Gamma gamma);
void Receiver(Epsilon epsilon);
void Receiver(Beta beta);
void Receiver(Alpha alpha);
我在这里需要一些不寻常的东西
我希望能够将Alpha对象传递给所有方法,但我不希望像void Receiver(Beta beta);
这样的方法能够接收从Beta
继承的类型的对象(也就是说,我想要,最糟糕的是,如果传递Exception
或Gamma
对象,请提高Epsilon
。
我怎样才能以足够通用的方式最好地实现它?
我正在考虑第一部分,我应该有一个允许向上转换的方法,例如
class Alpha
{
public T Upcast<T>() where T : Alpha
{
// somehow return a T
}
}
这里的问题是我希望这在所有级别上都有效,例如我希望能够“向上”Beta
。
第二部分应该更容易,因为我只会抛出超出我需要的类型的传递对象。
所以这样的事情就足够了:
void Receiver(Epsilon epsilon)
{
// if epsilon inherits from Epsilon, throw.
}
你可以帮我解决问题的第一部分吗?谢谢!
答案 0 :(得分:2)
您似乎正在努力处理可以处理给定类型的代码,但不能信任它可以正确处理子类型。一般来说,这违反了许多OOP原则,其中之一是LSP或Liskov Substitution Principle。
您可能会在某个地方考虑visitor pattern作为克服此问题的方法。它与您已经拥有的非常接近,只需稍加修改,这很好!当模式从你的代码中出现时,它很好,而不是强加于它。
您的void Receiver
方法基本上已经是访问者实现,所以让我们重命名它们并创建一个接口。
interface IVisitor
{
void Visit(Gamma gamma);
void Visit(Epsilon epsilon);
void Visit(Beta beta);
void Visit(Alpha alpha);
}
处理每种元素类型处理的类只需要实现该接口。
class Visitor : IVisitor
{
public void Visit(Gamma gamma) { Console.WriteLine("Visiting Gamma object"); }
public void Visit(Epsilon epsilon) { Console.WriteLine("Visiting Epsilon object"); }
public void Visit(Beta beta) { Console.WriteLine("Visiting Beta object"); }
public void Visit(Alpha alpha) { Console.WriteLine("Visiting Alpha object"); }
}
然后让每个元素类型也实现一个简单而通用的接口
interface IElement { void Accept(IVisitor visitor); }
class Alpha : IElement
{
public virtual void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
class Beta : Alpha
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
// etc.
(严格来说,接口不是必要的,它只是一个好形式。)
然后你去了一个双重调度的方法,其中一个元素调用一个特定于它自己的类型的访问者方法,即使你可能通过一个基类型引用它。
Alpha alpha = new Beta();
IVisitor visitor = new Visitor();
alpha.Accept(visitor);
如果您已正确实施,则应在屏幕上看到“访问Beta对象”(或者,根据您的意思,您应该看到所执行的“Receiver”行为。)
您可能考虑的另一个选项是简单地在基类上创建Receiver
虚拟方法,让派生类覆盖它,并在类中包含行为,如果这对您的层次结构有意义的话。如果行为需要是外部的,您还可以考虑一种知道如何为给定类创建特定处理器的工厂方法。如果将子类型传递给期望基础的方法,那么您有许多选项不会让您的程序变得疯狂。
我需要一些不寻常的东西。我希望能够通过 Alpha反对所有方法,但我不想要像void这样的方法 接收者(Beta beta);能够接收那些类型的对象 继承自Beta(也就是说,我希望,在最坏的情况下,如果 传递Gamma或Epsilon对象。
这确实非常奇怪。而且我不确定你真的想要那个。你好像在它的头上翻转了继承层次结构,超级类型可以代替派生类型,而不是相反,你应该真的重新思考你想要做的事情。
答案 1 :(得分:0)
您可以执行GetType并检查其是否为测试版。
答案 2 :(得分:0)
我希望能够将Alpha对象传递给所有方法
你不能可靠地做到这一点。 Alpha不是Gamma,因此您无法可靠地将其用作Receiver(Gamma)的参数。否则你可以这样做:
Alpha a = new Gamma(); // legal
Receiver((Epsilon)a); // not legal
如果您像在问题中一样定义接收器功能,编译器将根据声明的类型选择正确的方法(如果你使用它,则选择转换类型):
Epsilon e = new Epsilon();
Alpha a = new Gamma();
Receiver(e); // calls Receiver(Epsilon)
Receiver(a); // calls Receiver(Alpha)
Receiver((Gamma)a); // calls Receiver(Gamma)
Receiver((Beta)e); // calls Receiver(Beta)
因此,如果正确调用它,编译器将选择正确的方法。
但我不希望像
void Receiver(Beta beta);
这样的方法能够接收从Beta继承的类型的对象
然后你没有正确使用继承。 Gamma
是 Beta
。为什么要阻止继承的类型使用该方法?如果您希望Gamma和Beta共享一些没有继承的属性/方法,请选择其他模式,例如封装或组合并使用接口。
那就是说,你的Upcast功能应该很简单:
public T Upcast<T>() where T : Alpha
{
return this as T; // will return null is this is not a T
}
...
new Beta().Upcast<Gamma>(); // will return null
new Epsilon().Upcate<Epsilon>(); // will return the Epsilon