Как вызвать явно реализованный интерфейс-метод в базовом классе
У меня есть ситуация, когда два класса (один из другого) реализуют один и тот же интерфейс явно:
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; }
}
Я также могу изменить его, чтобы реализовать интерфейс неявно. Но я просто задавался вопросом, возможно ли это без каких-либо этих обходных решений.
Ответы
Ответ 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);
}