Подтвердить что ты не робот

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

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

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 implementation

Есть ли способ сделать это, без необходимости реализации другого (неинтерпретно-явного) вспомогательного метода?


Обновление

Я знаю, что это возможно с помощью "вспомогательного" метода, который может быть вызван производным классом, например:

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

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

4b9b3361

Ответ 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...

Ответ 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 && 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>

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

Ответ 3

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

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

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

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

Ответ 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

Вот моя версия Роланда Пихлакаса. Эта версия поддерживает целую цепочку наследования вместо базового класса. Метод Invoke включает в себя дополнительные параметры, и для нефункциональных методов существует Invoid типа 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);
}