【问题标题】: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; }
}

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

【问题讨论】:

    标签: c# interface base-class explicit-implementation


    【解决方案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 中实现它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-04
          • 2012-03-23
          • 2010-10-19
          • 2016-04-27
          • 2016-02-01
          • 2011-03-21
          • 2014-06-17
          • 2018-02-08
          相关资源
          最近更新 更多