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