Как вызвать явно реализованный интерфейсный метод в базовом классе - PullRequest
29 голосов
/ 12 мая 2011

У меня есть ситуация, когда два класса (один из которых является производным от другого) явно реализуют один и тот же интерфейс:

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

Я также могу изменить его, чтобы реализовать интерфейс не явно. Но мне было просто интересно, возможно ли это без этих обходных путей.

Ответы [ 6 ]

21 голосов
/ 12 мая 2011

К сожалению, это невозможно.
Даже с помощью вспомогательного метода. У вспомогательного метода те же проблемы, что и во второй попытке: this имеет тип B даже в базовом классе и вызовет реализацию M в B:

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(); }
}

Единственный обходной путь - это вспомогательный метод в A, который используется в реализации A M:

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(); }
}

Но вам нужно будет предоставить такой метод и для B, если будет class C : B, I ...

10 голосов
/ 20 августа 2012

Возможно использование отражения.
Код следует. Я добавил кеширование в качестве базовой оптимизации, но его можно оптимизировать, используя Delegate.CreateDelegate на methodInfo. Кроме того, число параметров и проверки типов могут быть добавлены с помощью 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>

Здесь - источник моего вдохновения.

0 голосов
/ 23 октября 2017

Вот моя версия хорошего решения Роланда Пихлакаса.Эта версия поддерживает всю цепочку наследования вместо непосредственного базового класса.Метод Invoke включает в себя дополнительные параметры, и для нефункциональных методов существует Invoke типа void.

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);
}
0 голосов
/ 04 сентября 2016
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();
        }
    }
}
0 голосов
/ 29 января 2015

Вы не можете вызвать явный интерфейсный метод в базовом классе, здесь я решил эту проблему

У меня есть два интерфейса -> 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
}
0 голосов
/ 12 мая 2011

это необходимо явно? ... Можете ли вы использовать абстрактный интерфейс или класс вместо интерфейса?

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

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

Я понятия не имею, что это невозможно, база вызововметод, когда это происходит от реализации интерфейса.

...