如何从超类调用两个子类的公共名称方法

本文关键字:子类 方法 两个 超类 调用 | 更新日期: 2023-09-27 18:20:11

在我的日常工作时间里,我遇到了一个问题:

假设:

  1. 我们有一个对象a,它是Object类型
  2. A可能是类型B或类型C
  3. 类型BC具有相同的名称方法GetSomething()
  4. 我们不知道BC是否都实现了相同的接口(这意味着我们不知道它们之间的关系)
  5. 类型BC继承自Object,这没有任何帮助。除了相同的方法名称外,我们对类型BC一无所知

问题是,我想从A调用GetSomething,无论它是什么类型:

//Object A maybe type B or C
//both B and C can call method
//but we just don't know type of A
var result=A.GetSomething();

如果你遇到这种情况,你会怎么做?

如何从超类调用两个子类的公共名称方法

这似乎需要一个抽象的方法

public abstract class A {
   protected abstract object GetSomething();
}

在每个派生类中,您需要实现GetSomething(),例如

public class B : A {
   protected override object GetSomeTHing(){
      //implementation goes here
   }
}

然后,您可以在实现超类(A)的任何位置自由调用GetSomething()

如果你不是在超级类的实现中寻找调用它的方法,而是在其他地方,你基本上有三个选项

  • 更改超级类的实现,以包含该方法的(潜在抽象)定义
  • 使用动态键入
  • 使用反射

前者与上述示例基本相同(只需使访问修饰符public而不是protected

第二种和第三种选择都有相同的缺点。它们不是编译时类型的,因此在开发过程中可能偶尔会出现运行时错误,而不是编译时错误。如果你的测试覆盖率很高,那应该不会有太大问题。

我更喜欢使用动态打字,因为它比反射版更容易读写。假设GetSomething返回一个int,它看起来像这个

int result=((dynamic)A).GetSomething();

我已经将结果更改为显式类型,而不是隐式类型,因为否则它将被类型化为dynamic,从而导致任何涉及结果的表达式也将被动态类型化。如果你知道GetSomething的返回类型,你最好告诉编译器前面的

  • 类型B和C具有相同的名称方法GetSomething()
  • 我们不知道B和C是否都实现了相同的接口

你不知道,但你能改变吗?因为最合乎逻辑的解决方案是描述接口中的"某物"行为:

interface IHasSomething
{
    Something GetSomething();
}

并将其应用于B类和C类。

假设你的B和C实例被声明为object,你可以试着测试它们是否实现了这个接口:

var something = obj as IHasSomething
if (something != null)
{
    var youWereLookingFor = something.GetSomething();
}

或者,您可以尝试使用反射,这应该是与良好的OO设计相反的最后手段(如果您想添加参数和/或重载,调用另一个方法,使用不同的返回类型,等等)。

如果A没有该方法,您就不应该考虑这样做,因为这是一个糟糕的设计,违反了OOP原则。然而,如果你绝对必须这样做,你可以尝试使用反射:

MethodInfo methodInfo = this.GetType().GetMethod("GetSomething", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
if(methodInfo != null)
   result = methodInfo.Invoke(this, new object[] {});

但是,我强烈建议在A类中使用抽象类或默认GetSomething()。。。

在一个好的体系结构中,您可以通过接口或公共基类访问该方法。

如果你真的想/需要这样做,那么你试图做的就是鸭子打字。您可以使用dynamic关键字或反射。

我能想到两种方法。直接检查"a"的类型是否实现了预期类型:

if(a is B)
{
    ((B)a).GetSomething();
}
else if(a is C)
{
    ((C)a).GetSomething();
}

否则,将强制转换为动态,并调用该函数。如果找不到函数,就会抛出。

dynamic dA = a;
int result;
try
{
    result = dA.GetSomething();
}
catch(RuntimeBinderException ex)
{
    // a was of a type that doesn't have GetSomething
}

第一个示例可以使用,第二个动态示例只能被视为示例,除非它是最后的手段。请尝试使用定义GetSomething()的抽象类或接口。