Как вызвать явно реализованный метод интерфейса в базовом классе

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

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

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


person M4N    schedule 12.05.2011    source источник


Ответы (7)


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

person Daniel Hilgarth    schedule 12.05.2011
comment
Да, это обходной путь - это то, что я добавил в обновленный вопрос. Но это не то, что я хотел знать. - person M4N; 12.05.2011
comment
@ M4N: Это просто для того, чтобы сделать ответ немного больше ;-) Настоящий ответ - первое предложение: это невозможно без таких обходных путей. - person Daniel Hilgarth; 12.05.2011
comment
Чтобы добавить, почему это невозможно: явно реализованные методы интерфейса сделаны закрытыми, что делает невозможным их вызов из производного класса. Вы, вероятно, должны предпочесть другой метод, подобный тому, который перечислил Даниэль, чтобы избежать запаха кода call super. - person Mark H; 12.05.2011
comment
Спасибо, что объяснили, почему это невозможно, теперь я понимаю. - person Amedio; 12.05.2011
comment
@DanielHilgarth Спасибо за пост, это сэкономило мне время. - person Etch; 03.10.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>

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

person Roland Pihlakas    schedule 20.08.2012
comment
Работает как шарм. Рефлексия в этом случае — выход. - person Kees C. Bakker; 01.02.2013
comment
Если вы можете что-то сделать, это не значит, что вы должны это делать. - person Unknown; 25.03.2013
comment
Тот факт, что вам, вероятно, следует подумать о том, чтобы не делать что-то, не означает, что нет случаев, когда это необходимо ;-) @Roland, спасибо, что поделились, это работает! - person Dav; 22.05.2014

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

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

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

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

person Martin Drlík    schedule 12.05.2011
comment
Нет! B имеет другую реализацию интерфейса, чем A (он что-то добавляет к реализации A). - person M4N; 12.05.2011
comment
@ M4N - Тогда вы можете только расширить A, чтобы переопределить метод, сделать базовый вызов метода и продолжить реализацию метода. - person Amedio; 12.05.2011
comment
Смотрите обновленный вопрос. (явная реализация не нужна, но мне просто интересно, возможно ли это) - person M4N; 12.05.2011

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

У меня есть два интерфейса -> 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
}
person Boopathi.Indotnet    schedule 29.01.2015

Вот моя версия красивого решения Роланда Пихлакаса. Эта версия поддерживает всю цепочку наследования вместо непосредственного базового класса. Метод 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);
}
person Xtro    schedule 23.10.2017

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

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.

person Steve    schedule 22.01.2020

person    schedule
comment
Добавьте некоторые пояснения - person HaveNoDisplayName; 04.09.2016