向上助手的方法?

时间:2012-01-12 00:59:07

标签: c# casting

我有以下课程:

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继承的类型的对象(也就是说,我想要,最糟糕的是,如果传递ExceptionGamma对象,请提高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.
}

你可以帮我解决问题的第一部分吗?谢谢!

3 个答案:

答案 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