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

#c# #interface #base-class #explicit-implementation

#c# #интерфейс #базовый класс #явная реализация

Вопрос:

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

 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 даже в базовом классе и вызовет реализацию 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

Комментарии:

1. Да, это обходное решение — это то, что я добавил к обновленному вопросу. Но это не то, что я хотел знать.

2. @M4N: Это просто для того, чтобы сделать ответ немного больше 😉 Реальный ответ заключается в первом предложении: это невозможно без таких обходных путей.

3. Чтобы добавить к тому, почему это невозможно: явно реализованные интерфейсные методы сделаны закрытыми, что делает невозможным их вызов из производного класса. Вероятно, вам следует предпочесть другой метод, подобный тому, который перечислил Дэниел, в любом случае, чтобы избежать запаха вызова super code.

4. Спасибо за объяснение, почему это невозможно, теперь я понимаю.

5. @DanielHilgarth Спасибо за сообщение, это сэкономило мне некоторое время.

Ответ №2:

Это возможно с помощью отражения.

Далее следует код. Я добавил кэширование в качестве базовой оптимизации, но его можно оптимизировать дополнительно с помощью Delegate.CreateDelegate on 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 amp;amp; 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>
  

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

Комментарии:

1. Работает как по волшебству. В этом случае лучше всего использовать рефлексию.

2. То, что вам, вероятно, следует подумать о том, чтобы чего-то не делать, не означает, что нет случаев, когда это необходимо 😉 @Roland, спасибо, что поделился, работает отлично!

Ответ №3:

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

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

http://msdn.microsoft.com/en-us/library/hfw7t1ce (v= против71).aspx

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

Комментарии:

1. Нет! B имеет другую реализацию интерфейса, чем A (это добавляет что-то к реализации A).

2. @M4N — Тогда вы можете только расширить переопределение метода и выполнить базовый вызов метода и продолжить реализацию метода.

3. Смотрите обновленный вопрос. (явная реализация не обязательна, но мне просто интересно, возможно ли это)

Ответ №4:

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

Комментарии:

1. Добавьте некоторое объяснение

Ответ №5:

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

        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 amp;amp; X.IsPrivate amp;amp; (X.Name == MethodName || X.Name.EndsWith("."   MethodName)));
        }

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

        return Resu<
    }

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

Ответ №6:

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

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

Выполнение этого позволит вам получить доступ к методу базовых классов без необходимости внедрять его в MySyb.

Ответ №7:

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

 interface I
{
    int M();
}

class A : I
{
    int I.M() { return M(); }

    protected virtual int M()
    {
        return 1;
    }
}

class B : A, I
{
    protected override int M()
    {
        return 2;
    }
}
  

Ответ №8:

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

У меня есть два интерфейса -> Интерфейс1 и Интерфейс2

 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
}