0

给定以下类结构:

class Base {}
class DerivedA: Base {}
class DerivedB: Base {}
class Container
{
    public List<Base> Items { get; }
}

随着我的应用程序开发更多功能,派生对象列表会随着时间的推移而增长。我正在尝试做:

Container container = ReturnsContainer();
foreach (var item in container.Items)
{
    doStuff(item);
}

其中 doStuff(item) 被派生类型重载。

void doStuff(DerivedA) {}
void doStuff(DerivedB) {}

这不起作用,因为编译器说“无法从'Base'转换为'DerivedA'”和“最佳重载方法匹配doStuff(DerivedA)有一些无效参数”。我能想到的最好方法是:

Container container = ReturnsContainer();
foreach (var item in container.Items)
{
    if (item is DerivedA)
    {
        doStuff((DerivedA)item);
    }
    else if (item is DerivedB)
    {
        doStuff((DerivedB)item);
    }
}

关于如何使这个更清洁的任何想法?由于我的 Derived 类型只会随着时间的推移而增长,因此我必须返回并添加到这个 long if 结构才能继续进行这项工作。

我认为随着派生类型数量的增加,container.Items.OfType<> 解决方案将转化为不断增长的(和不必要的)性能损失。

我必须为此使用通用代表吗?对于感觉应该是简单的多态性的东西来说,这似乎是一个过于复杂的解决方案。

编辑:为了更清楚起见,假设 Base 和 Derived 类型层次结构位于单独的 API 层上,并且不可修改(最终类)。该解决方案必须使用现有的继承结构,并且不能扩展 Base 和 Derived 对象。尽管随着时间的推移,API 层可以增长新的派生类。

4

4 回答 4

3

您要么必须使用运行时类型信息(例如反映类型)来确定调用哪个函数,要么可以使用doStuff在基类上声明并在派生类中实现的抽象方法( )。在许多情况下,您希望避免使用反射的解决方案,但如果您无法修改所涉及的类,则没有其他选择,并且使用dynamic其他答案中提出的方法是实现您想要的非常简单的方法。

例如实现两个重载:

void doStuff(DerivedA a) {
  ...
}

void doStuff(DerivedB b) {
  ...
}

并从循环中调用它们:

foreach (dynamic item in container.Items)
  doStuff(item);
于 2012-11-28T15:15:56.353 回答
2

这里有几个选项,但归根结底doStuuf是如果将来要添加新的派生类型,怎么知道该怎么做?

因此,一种选择是,如果Container确实一次只包含一种类型,那么您可以将其设为通用:

class Container<T> where T: Base
{
    public List<T> Items { get; }
}

现在当你迭代它时,你知道这Items是一个DerivedADerivedB

Container<DerivedA> container = ReturnsContainerOfDerivedA();
foreach (var item in container.Items)
{
    doStuff(item); // item is definately DerivedA
}

这意味着您要么有多种doStuff方法

public void doStuff(DerivedA item){...}
public void doStuff(DerivedB item){...}

或者如果更合适,您可以使用 1 以作为基础

public void doStuff(Base item){...}

就是简单多态的优点!


如果您使用的是 C#3.5 ,则第二个选项是定义doStuff为采取dynamic

public void doSomething(dynamic item){..}

但我个人会避免这个选项!

于 2012-11-28T15:18:08.823 回答
1

最简单的解决方案(不改变Base/DerivedA等)是将您的对象转换为dynamic

foreach (dynamic item in container.Items)
{
    doStuff(item);
}

所以实际调用的方法是在运行时确定的。

于 2012-11-28T15:18:44.897 回答
1

对象的用户Base应该需要知道Base它是什么类型的对象。在这种情况下,很明显它Base应该是抽象的,它应该有一个抽象方法来定义对象doStuff需要的任何行为Base。(如果Base不能选择抽象,您可以让它定义一个没有要覆盖的主体的虚拟方法,或者您可以让所有派生对象实现一个接口。)

完成后,每个派生对象都可以覆盖定义的方法,以提供每种类型之间的任何不同行为。

完成后,Container不需要将对象转换/转换为 以外的任何东西Base,每个对象Base都已经拥有单个doStuff方法所需的所有信息。

于 2012-11-28T15:24:11.887 回答