【问题标题】:Is there any way to call the parent version of an overridden method? (C# .NET)有什么方法可以调用重写方法的父版本? (C#.NET)
【发布时间】:2023-04-02 21:07:01
【问题描述】:

在下面的代码中,我尝试了两种方法来访问methodTwo的父版本,但结果总是2。有没有什么方法可以在不修改这两个类的情况下从ChildClass实例中获取1的结果?

class ParentClass
{
    public int methodOne()
    {
        return methodTwo();
    }

    virtual public int methodTwo()
    {
        return 1;
    }
}

class ChildClass : ParentClass
{
    override public int methodTwo()
    {
        return 2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new ChildClass();
        Console.WriteLine("a.methodOne(): " + a.methodOne());
        Console.WriteLine("a.methodTwo(): " + a.methodTwo());
        Console.WriteLine("((ParentClass)a).methodTwo(): "
         + ((ParentClass)a).methodTwo());
        Console.ReadLine();
    }
}

更新 ChrisW 发布了这个:

在课外,我不知道 任何简单的方法;但是,也许,我不 知道如果你尝试会发生什么 反射:使用 Type.GetMethod 查找 MethodInfo 的方法 与方法相关的 ParentClass,然后调用 MethodInfo.Invoke

那个答案被删除了。只是出于好奇,我想知道这种 hack 是否可行。

【问题讨论】:

标签: c# .net inheritance


【解决方案1】:

ChildClass.methodTwo()内,可以拨打base.methodTwo()

在课堂之外,拨打((ParentClass)a).methodTwo()拨打ChildClass.methodTwo。这就是虚方法存在的全部原因。

【讨论】:

  • ..,以及它被过度使用/误用的所有方式。
  • Michael - 你否决了某人对多态行为的解释?克雷格只是在解释为什么事情会以他们的方式运作,不要仅仅因为他们解释了一个你碰巧认为被过度使用的概念而对好的答案投反对票。
  • 我猜他是在开玩笑。 :)
  • 没有投反对票。只是想在多态性的过度使用上投入我的 -1 美分。抱歉,如果不清楚。
【解决方案2】:

在 IL 级别,您可能会发出 call 而不是 callvirt,然后完成工作 - 但如果我们将自己限制在 C# ;-p (edit 该死!运行时阻止你:VerificationException:“操作可能会破坏运行时的稳定性。”;删除 virtual 并且它工作正常;太聪明了一半......)

ChildClass 类型中,您可以使用base.methodTwo() - 但是,这在外部是不可能的。你也不能超过一层 - 没有base.base.Foo() 支持。

但是,如果您使用方法隐藏禁用多态性,您可以获得您想要的答案,但原因很糟糕:

class ChildClass : ParentClass
{
    new public int methodTwo() // bad, do not do
    {
        return 2;
    }
}

现在您可以从同一个对象得到不同的答案,具体取决于变量是定义为ChildClass 还是ParentClass

【讨论】:

  • 为什么在这种情况下使用“新”不好?在任何情况下使用“新”是好的?
  • @PeterHuber 因为它破坏了多态性;在这种情况下,new 的主要用例是使类型更加专业化。例如,DbCommand 有一个public DbParameterCollection Parameters {get;} - 但SqlCommand : DbCommand 有一个public new SqlParameterCollection Parameters {get;}。两个 API 返回相同的 instance(通过单独的 protected abstract 属性实现,即 DbParameterCollection - 因此该值将始终 be SqlParameterCollection) - 但更多-specific 类型以更具体的方式公开它
  • @MarcGravell:感谢您提供的信息,虽然我不太了解它的细节。例如,我不知道属性是否可以是虚拟的和被覆盖的。方法更容易理解。我来到这个问题,因为我的基类需要在基类中调用另一个虚拟方法,而不是被覆盖的方法。我得到了一个stackoverflow,因为基类调用了覆盖版本,它再次调用了基类。通过将覆盖更改为新的,现在一切正常。我希望有一种方法可以指定需要调用基本虚拟方法,但这似乎是不可能的。
【解决方案3】:

如上所述,如果您需要在 PRODUCTION 代码中调用“base.base”,那么您的类设计就会出现问题。但是,如果您在使用无法编译的外部库时调试或搜索某些工作区,则使用此技术是非常合理的。令人不快的是 C# 不直接提供此选项。您仍然可以使用带有 IL 生成器和 Emit 的 Kenneth Xu 解决方案。它有效。

class A { public virtual string foo() { return "A"; } }

class B : A { public override string foo() { return "B"; } }

// now in class C
class C : B {}      
// we can call virtual method "foo" from A using following code
MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);

DynamicMethod baseBaseFoo = new DynamicMethod(
            "foo_A",
            typeof(string),
            new[] { typeof(A) },
            typeof(A));
        ILGenerator il = baseBaseFoo.GetILGenerator();
        il.Emit(OpCodes.Ldarg, 0);
        il.EmitCall(OpCodes.Call, fooA, null);
        il.Emit(OpCodes.Ret);

// call foo() from class A, it returns "A"
(string)baseBaseFoo.Invoke(null, new object[] { this });

有关参考和完整示例,请参阅 http://kennethxu.blogspot.cz/2009/05/cnet-calling-grandparent-virtual-method.html

谢谢徐肯尼斯!

【讨论】:

【解决方案4】:

正如 Mark Gravell 所说,不,不是在外部。但这是我用过的另一个技巧。 ChildClass 可以从ParentClass 暴露methodTwo() 以供自己使用或供外部使用。在你的情况下:

class ChildClass : ParentClass {
    override public int methodTwo() {
        return 2;
    }
    public int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}

// Now instead of
Console.WriteLine("ParentClass methodTwo: " + ((ParentClass)a).methodTwo());
// use
Console.WriteLine("ParentClass methodTwo: " + a.ParentClass_methodTwo());

在我的例子中,子类引入了 Peer 的概念,我需要覆盖它的 methodTwo() 来调用 peer 上的基本版本。然而,通过覆盖它,它隐藏了它......或者是它?这种技术来拯救。

class ChildClass : ParentClass {
    ChildClass peer;
    override public int methodTwo() {
        return peer.ParentClass_methodTwo();
    }
    private int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}

【讨论】:

    【解决方案5】:
    public class Parent 
    {
        public string Method()
        {
           return "parent";
    
        }
    }
    
    public class Child:Parent
    {
        public string Method()
        {
           return "child";
    
        }
    }
    

    上面的代码成功地覆盖了父方法,但父方法的值仍然没有改变。

    您可以使用以下代码从父类子类返回值 -

    Child child = new Child();
    string result = (((Parent)child).Method()) + child.Method();
    

    但 Visual Studio 会在编译时向您显示警告。

    【讨论】:

    • 你有推荐人吗,如果有就更好了。
    • 您的代码不使用实际继承(虚拟方法的覆盖)。它只是创建了一个同名的新函数,这不是所要求的。
    【解决方案6】:

    据我所知,一旦方法被覆盖,就不能调用父方法。

    【讨论】:

    • 这不是一个糟糕的答案 - 我认为他的意思是您不能从子实例调用覆盖的父方法,这就是问题的精神。
    【解决方案7】:

    我认为除非您直接创建 ParentClass 的实例,否则这是不可能的。 这就是继承、多态的本质......

    【讨论】:

      【解决方案8】:

      我偶然发现了这个寻求帮助,并最终采用了我自己的方法来调用方法的祖先版本。这更像是一种变通方法,假设您可以编辑祖先类:

      将祖先类中的相关功能放入带有必要参数的静态方法中。该类中的方法可以调用它,因此它不需要复制功能,并且如果需要,子类可以通过静态方法调用该功能。这样,它甚至可以在一个方法中完成,并且可以引用它想要调用的特定类。此外,它可以访问比单纯的父母更远的祖先,这是使用“base”的限制。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-21
        相关资源
        最近更新 更多