C#子类型已强制转换为超类型,但仍希望调用该子类型方法

本文关键字:类型 希望 方法 调用 转换 超类 | 更新日期: 2023-09-27 18:19:38

我想知道以下是否可能:

class A
{
    public int SomeMethod()
    {
        return 1;
    }
}
class B : A
{
    public override int SomeMethod()
    {
        return 3;
    }
}
class DrivingClass
{
    public static void Main()
    {
        B classB = new B();
        A classA = (A)classB;
        Assert.IsEqual(3, classA.SomeMethod()); 
    }
}

这当然不能预期3,但实际是1。有没有一种方法(不需要将其类型转换回B)让classA.SomeMethod()调用重写的版本,因为它一开始是B(尽管我认为这个知识一旦转换就会丢失)。

更新:A类已经写好了,不能出于任何目的进行编辑。我只能控制类B。知道类B将被转换为类型A,我只想在对我的类型转换的B调用SomeMethod()时使用我的实现。

C#子类型已强制转换为超类型,但仍希望调用该子类型方法

SomeMethod需要在A中声明为虚拟,才能在B中成功地覆盖

 public virtual int SomeMethod() // in A
 public override int SomeMethod() // in B

有了这个正确的

 A a = new B();
 int value = a.SomeMethod(); 
 Debug.Assert(value == 3); // succeeds

如果您

 public int SomeMethod() // in A
 public new int SomeMethod() // in B, or just 
 public int SomeMethod() // in B

则上述断言失败。B中的方法隐藏了基本方法,但仅通过B引用。当在A的参考下操作时,你会得到基本的行为。

 A a = new B();
 int value = a.SomeMethod(); // gets 1 from A, not 3 from B

UPDATE:类A已经编写好了,对于所有的意图和目的都不能编辑。我只能控制类B。知道类B将被转换为类型A,我只想在对我的类型转换的B调用SomeMethod()时使用我的实现。

默认情况下,C#中的方法不是虚拟的。如果A的作者在设计它时没有考虑到可扩展性(至少,就覆盖SomeMethod而言),那么当它被视为A时,您将无法用B替换或覆盖该行为。但是,如果您可以控制强制转换,或者更确切地说是代码的依赖关系,您可以将其反转,以便A实际上遵守B的约定,而不是相反。例如,考虑适配器模式

interface IB
{
    int SomeMethod();
}
class B : IB
{
    public int SomeMethod() { return 3; }
}
class ABAdapter : IB
{
    private A a; 
    public ABAdapter(A a) { this.a = a; }
    public int SomeMethod() { return a.SomeMethod(); } 
}

在本例中,您已经使用适配器模式使A通过IB接口实际履行了B的约定。因此,曾经可能依赖于AB的代码现在可以依赖于IB。ABAdapter只是委托给A实现。

public void DoSomething(IB ib) // given 
A a = new A();
DoSomething(new ABAdapter(a)); // invoke with A
DoSomething(new B()); // invoke with B

您还没有在A中声明SomeMethod是虚拟的,所以这显然不是您的真实代码(否则B无法覆盖它),但当您进行更改时,它将返回3。如果不这样做,多态性就会被彻底打破。

现在,我们可以对你发布的代码进行的另一个更改是在B中。如果你使用:

public new int SomeMethod()
{
    return 3;
}

然后您的断言将失败,因为它将调用A.SomeMethod(),而B.SomeMethod()遮蔽或隐藏。基本上,如果您想要多态行为,您需要使用override,并且它必须在虚拟方法上。

我建议阅读此处的主题在文章中,它展示了以下代码示例:

class A
{
   public void F() { Console.WriteLine("A.F"); }
   public virtual void G() { Console.WriteLine("A.G"); }
}
class B: A
{
   new public void F() { Console.WriteLine("B.F"); }
   public override void G() { Console.WriteLine("B.G"); }
}
class Test
{
   static void Main() {
      B b = new B();
      A a = b;
      a.F(); //prints A.F
      b.F(); //prints B.F
      a.G(); //prints B.G (due to virtual method override)
      b.G(); //prints B.G
   }
}

鉴于您的编辑声明您无法重新定义类A,恐怕您或多或少有些运气不好。

可以从A派生B,然后声明new int SomeMethod(),但不能使用A引用调用该方法。您可以使用运行时类型检查:

int CallSomeMethod(A obj)
{
    var b = obj as B;
    return b == null ? obj.SomeMethod() : b.SomeMethod();
}