【问题标题】:How to call base method from derived class instance in C#?如何从 C# 中的派生类实例调用基方法?
【发布时间】:2017-05-05 06:22:34
【问题描述】:

我有 C# 代码,基础 class A 和派生 class B

public class A
{
    public virtual void print() {Console.WriteLine("a"); }
}
public class B:A
{
    public override void print() { Console.WriteLine("b"); }
}

static void Main(string[] args)
{
    A a= new B();
    //In c++ I can use .Base:: to call base method from derived class instance
    a.Base::print();
}

我无法修改这两个类,我不知道我可以在 C# 中做什么,有什么建议吗?


另外,感谢大家参与讨论,我想澄清一下为什么我需要这种行为:

在.net 框架中,我们有一个接口IPostBackDataHandler 来处理回发。里面有一个方法

public bool LoadPostData( string postDataKey, NameValueCollection postCollection )

当我实现它并测试时,我发现有时给定的回发类型 postCollectionNameValueCollection ,而其他时候是 HttpValueCollection (a NameValueCollection 的派生类)

那么,如果它是一种 HttpValueCollection,当我从中获取项目时,例如。 postCollection['ControlID'] 并且我在此控件中输入 htmlHttpValueCollection.get_item() 将始终验证输入并将其视为缺点。虽然 NameValueCollection.get_item() 不会

我不希望它自动进行验证,至少应该决定它是否应该被验证,不是吗?

【问题讨论】:

  • 也许dup dup2
  • 本质上,您希望编译器发出call 而不是callvirt。不幸的是,目前没有直接在 C# 中请求的方法。

标签: c# inheritance


【解决方案1】:

无法从派生类外部访问基方法。 您可以在派生类中编写一个调用基方法的方法,如下所示:

public class B : A
{
    public override void print() { Console.WriteLine("B"); }

    public void basePrint() { base.print(); }
}

或者您可以使用Reflection 获取基本方法定义并调用它,但它相当难看。 以下是如何创建调用基本print 方法的DynamicMethod

// Get MethodInfo of the base method
var methodInfo = typeof(A).GetMethod("Print", BindingFlags.Instance | BindingFlags.Public);

// Create DynamicMethod based on the methodInfo
var dynamicMethod = new DynamicMethod("BasePrint", methodInfo.ReturnType, new[] { methodInfo.DeclaringType }, methodInfo.DeclaringType);
// Create ILGenerator for the dynamic method
var il = dynamicMethod.GetILGenerator();

// Emit argument with index 0 - DeclaringType
il.Emit(OpCodes.Ldarg_0);
// Emit method call
il.EmitCall(OpCodes.Call, methodInfo, null);
// Emit method return value
il.Emit(OpCodes.Ret);

// Invoke method with target...
var b = new B();
dynamicMethod.Invoke(null, new object[] {b});

// ... or create a delegate and invoke method without target
var action = dynamicMethod.CreateDelegate(typeof(Action)) as Action;
action.Invoke();

注意它只适用于无参数方法。如果你想用参数调用方法,你必须用DeclaringType 将它们的类型放入数组中,然后将它们全部发出。如果方法返回某些内容,您还必须创建 Action<parameterTypesFunc<returnType, parameterTypes> 类型的委托。

【讨论】:

  • 可惜不能修改类本身。我猜连Reflection也只能实现类B的覆盖方法吧?可以吗?
  • 这是可能的。您可以从基类获取methodInfo 并构造DynamicMethod。然后,使用ILGenerator 你应该可以调用这个方法。我不知道上下文,但也许你可以创建另一个派生自A 的类(比如说C),然后在其中调用base.print()
  • 我更新了我的答案,解释了如何生成和创建 dynamicMethod。
【解决方案2】:

您只能通过以下方式从派生类 (B) 调用基类的方法:

base.print();

我认为没有办法做你想做的事,因为它有点打破了多态性。

但是,您可以在A隐藏print 方法,方法是在B 中执行此操作:

new public void print() { Console.WriteLine("b"); }

这样,这段代码:

A a= new B();
a.print();

将在A 中调用print

【讨论】:

  • Base 存在于哪个命名空间中?
  • c#中没有这样的东西
  • @LeiYang 不要使用大写B,它不是一个类它是一个关键字msdn.microsoft.com/en-us/library/hfw7t1ce.aspx
  • 你是对的,但我不能编辑课程本身.. :(
【解决方案3】:

感谢大家! 最后我意识到这种行为需要使用反射和委托,就像 nocodename 显示的一样,在这里我找到了类似的方法。

A a = new B();
var ptr = typeof(A).GetMethod("Print", new[] { typeof(_inputParameter) }).MethodHandle.GetFunctionPointer();
var baseGetItem = (Func<String, String>)Activator.CreateInstance(typeof(Func<String, String>), a, ptr);
String postedValue = baseGetItem(_parameter);

【讨论】:

    【解决方案4】:

    由于override 在 c# 中的工作方式,您不能在 B 类之外调用 base print,即从 Main 方法。但是,如果您希望有可能这样做,您应该考虑在B 类中使用 shadow 打印方法:

    public class B:A
    {
        public new void print() { Console.WriteLine("b"); }
    }
    

    使用 new 关键字 class shadows 来自父类的成员,因此您的代码行将起作用:

    A a= new B();
    a.print(); //prints "a"
    

    B b= new B();
    b.print(); //prints "b"
    

    【讨论】:

      【解决方案5】:

      覆盖的整个想法是您不会调用基类的函数。 如果你想调用 A.print - 使用 A 对象,如果你想调用 B.print 使用 B 对象,如果你想要一个可以使用其中一些和其中一些的对象 - 单独创建它或使用不同的函数名字,也许能解决你的问题。

      我不确定你想在这里完成什么,但也许你想了解Visitor Pattern

      祝你好运!

      【讨论】:

        【解决方案6】:

        您可以使用“base”键从派生类简单地调用基类方法:

        看例子,

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        
        namespace ConsoleApplication1
        {
            class Program
            {
                static void Main(string[] args)
                {
                    B obj= new B();
                    obj.print();
                    Console.ReadKey();
                }
            }
        
            public class A
            {
                public virtual void print() { Console.WriteLine("a"); }
            }
            public class B : A
            {
                public override void print() 
                {
                    base.print();
                    Console.WriteLine("b"); 
                }
            }
        
        }
        

        希望你能理解!谢谢你

        【讨论】:

          【解决方案7】:

          在继承中重写基方法时不能调用它。您可以将 B 中的方法设为 new 而不是 override,但您会丢失实现。

          public class A
          {
              public virtual void Print() { Console.WriteLine("a"); }
          }
          
          public class B: A
          {
              public new void Print() { Console.WriteLine("b"); }
          }
          

          打电话的时候这样

          static void Main(string[] args)
          {
              A a = new B();
              a.Print(); // prints "a"
          }
          

          否则,您可以在覆盖的方法中调用 base.Print()。

          public class A
          {
              public virtual void Print() { Console.WriteLine("a"); }
          }
          
          public class B: A
          {
              public override void Print() 
              {
                  base.Print();
                  Console.WriteLine("b"); 
              }
          }
          

          【讨论】:

            【解决方案8】:
            var obj = new B();
            obj.Print(); // overriden method
            (obj as A).Print(); // base method
            

            【讨论】:

            • 由于print 方法是虚拟的,我怀疑这是否有效?!?另请参阅this .NET Fiddle
            • 它没有。它甚至可能无法编译。但它确实调用了基本方法。
            • 它调用被覆盖的方法。
            • 虽然这段代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!
            猜你喜欢
            • 2016-07-06
            • 1970-01-01
            • 1970-01-01
            • 2012-04-16
            • 2019-07-25
            • 2013-04-11
            • 2018-11-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多