上铸辅助方法

本文关键字:方法 | 更新日期: 2023-09-27 18:20:14

我有以下类:

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继承的类型的对象(也就是说,如果传递了GammaEpsilon对象,我最坏的情况下希望引发Exception

我如何才能以足够通用的方式最好地实现这一点?

我在想,对于第一部分,我应该有一个允许向上广播的方法,比如

class Alpha
{
    public T Upcast<T>() where T : Alpha
    {
        // somehow return a T
    }
}

这里的问题是,我希望它能在所有级别上运行,例如,我也希望能够"升级"Beta

第二部分应该更容易,因为我只会抛出传递的对象,这些对象超出了我需要的类型。

这样的东西就足够了:

void Receiver(Epsilon epsilon)
{
    // if epsilon inherits from Epsilon, throw.
}

你能帮我解决问题的第一部分吗?谢谢

上铸辅助方法

您似乎在努力处理能够处理给定类型的代码,但不能相信它能够正确处理子类型。一般来说,这违反了许多OOP原则,其中之一是LSP,或Liskov替代原则。

你可能会认为访客模式是克服这个问题的一种手段。它非常接近你已经拥有的,有一些小的修改,这很好!当模式从你的代码中出现,而不是被迫出现时,这很好。

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作为基类上的虚拟方法,并让派生类覆盖它,并在类内具有行为(如果这对您的层次结构有意义的话)。如果行为需要是外部的,您还可以考虑一种工厂方法,它知道如何为给定的类创建特定的处理器。如果将子类型传递给期望基的方法,则有许多选项不会使程序发疯。


我需要一些不寻常的东西。我希望能够通过Alpha对象到所有方法,但我不想要void这样的方法接收器(Beta-Beta);能够接收类型为从Beta继承(也就是说,在最坏的情况下,如果传递Gamma或Epsilon对象。

这确实很奇怪。我不确定你是否真的想要。您似乎已经颠倒了继承层次结构,其中超类型可以替换派生类型,而不是相反,您应该真正地重新思考您正在尝试做什么。

您可以执行GetType并检查它是否是测试版。

我希望能够将Alpha对象传递给的所有方法

你不能可靠地做到这一点。Alpha不是Gamma,因此不能可靠地将其用作接收器(Gamma)的参数。否则你可以这样做:

Alpha a = new Gamma(); // legal
Receiver((Epsilon)a);    // not legal

如果你像在问题中那样定义Receiver函数,编译器将根据声明的类型(或者如果你强制类型,则选择强制类型)选择正确的方法:

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