C#子类型转换为超类型但仍希望调用子类型方法

时间:2012-01-04 20:52:32

标签: c# polymorphism override

我想知道以下是否可行:

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()时使用我的实现。

4 个答案:

答案 0 :(得分:3)

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

  

更新:已经编写了A类,并且无法编辑所有意图和目的。我只能控制B类。知道B类将被输入A类,我只想在我的类型化的B上调用SomeMethod()时使用我的实现。

默认情况下,C#中的方法不是虚拟的。如果A的作者没有考虑到可扩展性(至少,至于覆盖SomeMethod),那么当B时,您将无法替换或覆盖该行为。它被视为A。但是,如果你能够控制强制转换,或者更确切地说是代码的依赖关系,你可以将其反转,以便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通过B接口实际履行IB的约定。因此,曾经依赖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

答案 1 :(得分:1)

您尚未在SomeMethod中声明A是虚拟的,所以这显然不是您的真实代码(否则B无法覆盖它)但是当您制作时如果发生变化,返回3.如果没有,多态将完全被破坏。

现在,我们对您发布的代码所做的另一项更改是在B.如果您使用:

public new int SomeMethod()
{
    return 3;
}

然后你的断言会失败 - 因为它会A.SomeMethod()通过{{1}调用阴影隐藏 },没有被覆盖。基本上,如果你想要多态行为,你需要使用B.SomeMethod(),它必须是虚拟方法。

答案 2 :(得分:0)

我建议您阅读here主题 在文章中,它显示了这些代码示例:

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
   }
}

答案 3 :(得分:0)

鉴于您的编辑声明您无法重新定义课程A,我担心您或多或少会失去运气。

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

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