如何在基类上调用显式实现的接口方法

Posted

技术标签:

【中文标题】如何在基类上调用显式实现的接口方法【英文标题】:How to call an explicitly implemented interface-method on the base class 【发布时间】:2011-08-23 23:45:09 【问题描述】:

我有一种情况,两个类(一个派生自另一个)都显式地实现了相同的接口:

interface I

  int M();


class A : I

  int I.M()  return 1; 


class B : A, I

  int I.M()  return 2; 

I.M()的派生类实现中,我想调用基类的实现,但我不知道该怎么做。到目前为止我尝试的是这个(在 B 类中):

int I.M()  return (base as I).M() + 2; 
// this gives a compile-time error
//error CS0175: Use of keyword 'base' is not valid in this context

int I.M()  return ((this as A) as I).M() + 2; 
// this results in an endless loop, since it calls B's implementation

有没有办法做到这一点,而不必实现另一个(非接口显式)辅助方法?


更新

我知道可以使用可由派生类调用的“帮助器”方法,例如:

class A : I

    int I.M()  return M2(); 
    protected int M2  return 1; 

我还可以更改它以非显式地实现接口。但我只是想知道如果没有这些变通方法是否有可能。

【问题讨论】:

【参考方案1】:

很遗憾,这是不可能的。 甚至没有辅助方法。辅助方法与您的第二次尝试存在相同的问题:this 的类型为 B,即使在基类中也会调用 B 中的 M 的实现:

interface I

  int M();

class A : I

  int I.M()  return 1; 
  protected int CallM()  return (this as I).M(); 

class B : A, I

  int I.M()  return CallM(); 

唯一的解决方法是在AM 实现中使用A 中的辅助方法:

interface I

  int M();

class A : I

  int I.M()  return CallM(); 
  protected int CallM()  return 1; 

class B : A, I

  int I.M()  return CallM(); 

但如果有class C : B, I,您也需要为B 提供这样的方法...

【讨论】:

是的,我在更新后的问题中添加了解决方法。但这不是我想知道的。 @M4N:那只是为了让答案更大一点;-) 真正的答案是第一句话:没有这样的解决方法是不可能的。 补充为什么这是不可能的:显式实现的接口方法被设为私有 - 使得它们无法从派生类调用。您可能应该更喜欢 Daniel 列出的另一种方法,以避免 call super 代码异味。 感谢您解释为什么这是不可能的,我现在明白了。 @DanielHilgarth 感谢您的帖子,这为我节省了一些时间。【参考方案2】:

可以使用反射。 代码如下。我添加了缓存作为基本优化,但可以通过在methodInfo 上使用Delegate.CreateDelegate 进一步优化它。此外,可以使用methodInfo.GetParameters() 添加参数计数和类型检查。

interface I   
   
    int M();   
 

class A : I   
   
    int I.M()  return 1;    
 

class B : A, I   
   
    BaseClassExplicitInterfaceInvoker<B> invoker = new BaseClassExplicitInterfaceInvoker<B>();
    int I.M()  return invoker.Invoke<int>(this, "M") + 2;    


public class BaseClassExplicitInterfaceInvoker<T>

    private Dictionary<string, MethodInfo> cache = new Dictionary<string, MethodInfo>();
    private Type baseType = typeof(T).BaseType;

    private MethodInfo FindMethod(string methodName)
    
        MethodInfo method = null;
        if (!cache.TryGetValue(methodName, out method))
        
            var methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

            foreach (var methodInfo in methods)
            
                if (methodInfo.IsFinal && methodInfo.IsPrivate) //explicit interface implementation
                
                    if (methodInfo.Name == methodName || methodInfo.Name.EndsWith("." + methodName))
                    
                        method = methodInfo;
                        break;
                    
                
               

            cache.Add(methodName, method);
        

        return method;
    

    public RT Invoke<RT>(T obj, string methodName)
                
        MethodInfo method = FindMethod(methodName);
        return (RT)method.Invoke(obj, null);
    

   //public static class BaseClassExplicitInterfaceInvoker<T>

Here是我灵感的源泉。

【讨论】:

像魅力一样工作。在这种情况下,反射是要走的路。 你可以做某事并不意味着你必须去做。 仅仅因为您可能应该考虑不做某事并不意味着没有这样做是必不可少的 ;-) @Roland,感谢分享,工作愉快!【参考方案3】:

有必要明确吗?...你可以使用抽象类或类代替接口吗?

interface ISample 
class A : ISample 
class B : A 
...
base.fun();
...

http://msdn.microsoft.com/en-us/library/hfw7t1ce(v=vs.71).aspx

当它来自接口的实现时,我不知道它不可能调用基方法。

【讨论】:

不! B 的接口实现与 A 不同(它为 A 的实现添加了一些东西)。 @M4N - 然后你只能扩展 A 覆盖该方法并对方法进行基本调用并继续实现该方法。 查看更新的问题。 (不需要显式实现,但我只是想知道这是否可能)【参考方案4】:

你不能在基类中调用显式接口方法,这里我解决了这个问题

我有两个接口 -> Interface1 和 Interface2

public interface Interface1
      
    string method2();      


public interface Interface2
   
    string method22();


主类方法

class Program

    static void Main(string[] args)
    

        class1 cls = new class1();
        string str = cls.method2();
    

和我的接口实现类

class class1 : Interface1, Interface2


    #region Interface1 Members

    public string method2()
    
        return (this as Interface2).method22();
          

    #endregion

    #region Interface2 Members      

    string Interface2.method22()
    
        return "2";
    

    #endregion

【讨论】:

【参考方案5】:
using System;

namespace SampleTest

    interface IInterface1
    
        void Run();
    

    interface IInterface2
    
        void Run();
    

    public class BaseClass : IInterface1, IInterface2
    
        public void Interface1Run()
        
            (this as IInterface1).Run();
        

        public void Interface2Run()
        
            (this as IInterface2).Run();
        

        void IInterface2.Run()
        
            Console.WriteLine("I am from interface 2");
        

        void IInterface1.Run()
        
            Console.WriteLine("I am from interface 1");
        
    

    public class ChildClass : BaseClass
    
        public void ChildClassMethod()
        
            Interface1Run();
            Interface2Run();      
        
    
    public class Program : ChildClass
    
        static void Main(string[] args)
        
            ChildClass childclass = new ChildClass();
            childclass.ChildClassMethod();
        
    

【讨论】:

添加一些解释【参考方案6】:

这是我对 Roland Pihlakas 的好解决方案的版本。这个版本支持整个继承链而不是直接基类。 Invoke 方法包含附加参数,并且有一个用于非函数方法的 void 类型 Invoke。

public class BaseClassExplicitInterfaceInvoker<T>

    readonly Dictionary<string, MethodInfo> Cache = new Dictionary<string, MethodInfo>();

    MethodInfo FindMethod(string MethodName)
    
        if (Cache.TryGetValue(MethodName, out var Result)) return Result;

        var BaseType = typeof(T);
        while (Result == null)
        
            if ((BaseType = BaseType.BaseType) == typeof(object)) break;

            var Methods = BaseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            Result = Methods.FirstOrDefault(X => X.IsFinal && X.IsPrivate && (X.Name == MethodName || X.Name.EndsWith("." + MethodName)));
        

        if (Result != null) Cache.Add(MethodName, Result);

        return Result;
    

    public void Invoke(T Object, string MethodName, params object[] Parameters) => FindMethod(MethodName).Invoke(Object, Parameters);
    public ReturnType Invoke<ReturnType>(T Object, string MethodName, params object[] Parameters) => (ReturnType)FindMethod(MethodName).Invoke(Object, Parameters);

【讨论】:

【参考方案7】:

启用选项是创建一个不显式实现接口的基类,并且只在子类上实现接口。

public class MyBase

    public void MethodA()
    
        //Do things
    


public interface IMyInterface

    void MethodA();
    void MethodB();



public class MySub: MyBase, IMyInterface

    public void MethodB()
    
        //Do things
    

这样做将允许您访问基类 MethodA,而无需在 MySyb 中实现它。

【讨论】:

以上是关于如何在基类上调用显式实现的接口方法的主要内容,如果未能解决你的问题,请参考以下文章

TestNg 在基类上的 @BeforeTest 每个夹具只发生一次

c++ 在基类类型的向量中调用子类方法

调用派生类的构造函数在基类的构造函数之前执行

Tomcat 没有在 Eclipse 中关闭

如何在基类中实现子类迭代器的统一接口?

虚方法抽象方法接口方法