Методы Виртуального Расширения?
У меня есть класс, который используется в клиентском приложении и в серверном приложении. В серверном приложении я добавляю некоторые функциональные возможности к методам расширения класса. Отлично работает. Теперь я хочу немного больше:
мой класс (B) наследуется от другого класса (A).
Я хотел бы прикрепить виртуальную функцию к A (скажем, Execute ()), а затем реализовать эту функцию в B. Но только на сервере. Метод execute() будет не только можно делать на сервере, используя типы, о которых знает только сервер.
существует много типов, которые наследуются от A так же, как B, и я хотел бы реализовать Execute() для каждого из них.
Я надеялся, что смогу добавить метод виртуального расширения к A, но эта идея, похоже, не летает. Я ищу самый элегантный способ решить эту проблему, с или без методов расширения.
6 ответов
нет, нет таких вещей, как методы виртуального расширения. Вы можете использовать перегрузку, но это не поддерживает полиморфизм. Похоже, вы можете посмотреть что - то вроде инъекции зависимостей (и т. д.), Чтобы иметь другой код (зависимости), добавленный в разных средах, и использовать его в обычных виртуальных методах:
class B {
public B(ISomeUtility util) {
// store util
}
public override void Execute() {
if(util != null) util.Foo();
}
}
затем используйте di framework для предоставления специфичного для сервера ISomeUtility
реализации B
во время выполнения. Вы можете сделать то же самое с центральным static
реестр (МОК, но не DI):
override void Execute() {
ISomeUtility util = Registry.Get<ISomeUtility>();
if(util != null) util.Foo();
}
(где вам нужно написать Registry
etc; плюс на сервере зарегистрируйте ISomeUtility
реализации)
вы можете использовать новые функции динамического типа, чтобы избежать необходимости создания реестра типов для методов:
using System;
using System.Collections.Generic;
using System.Linq;
using visitor.Extension;
namespace visitor
{
namespace Extension
{
static class Extension
{
public static void RunVisitor(this IThing thing, IThingOperation thingOperation)
{
thingOperation.Visit((dynamic)thing);
}
public static ITransformedThing GetTransformedThing(this IThing thing, int arg)
{
var x = new GetTransformedThing {Arg = arg};
thing.RunVisitor(x);
return x.Result;
}
}
}
interface IThingOperation
{
void Visit(IThing iThing);
void Visit(AThing aThing);
void Visit(BThing bThing);
void Visit(CThing cThing);
void Visit(DThing dThing);
}
interface ITransformedThing { }
class ATransformedThing : ITransformedThing { public ATransformedThing(AThing aThing, int arg) { } }
class BTransformedThing : ITransformedThing { public BTransformedThing(BThing bThing, int arg) { } }
class CTransformedThing : ITransformedThing { public CTransformedThing(CThing cThing, int arg) { } }
class DTransformedThing : ITransformedThing { public DTransformedThing(DThing dThing, int arg) { } }
class GetTransformedThing : IThingOperation
{
public int Arg { get; set; }
public ITransformedThing Result { get; private set; }
public void Visit(IThing iThing) { Result = null; }
public void Visit(AThing aThing) { Result = new ATransformedThing(aThing, Arg); }
public void Visit(BThing bThing) { Result = new BTransformedThing(bThing, Arg); }
public void Visit(CThing cThing) { Result = new CTransformedThing(cThing, Arg); }
public void Visit(DThing dThing) { Result = new DTransformedThing(dThing, Arg); }
}
interface IThing {}
class Thing : IThing {}
class AThing : Thing {}
class BThing : Thing {}
class CThing : Thing {}
class DThing : Thing {}
class EThing : Thing { }
class Program
{
static void Main(string[] args)
{
var things = new List<IThing> { new AThing(), new BThing(), new CThing(), new DThing(), new EThing() };
var transformedThings = things.Select(thing => thing.GetTransformedThing(4)).Where(transformedThing => transformedThing != null).ToList();
foreach (var transformedThing in transformedThings)
{
Console.WriteLine(transformedThing.GetType().ToString());
}
}
}
}
Я бы предложил что-то вроде следующего. Этот код можно улучшить, добавив поддержку обнаружения типов иерархии промежуточных классов, которые не имеют сопоставления диспетчеризации, и вызов ближайшего метода диспетчеризации на основе иерархии среды выполнения. Его также можно улучшить, используя отражение для обнаружения перегрузки ExecuteInteral () и автоматического добавления их на карту отправки.
using System;
using System.Collections.Generic;
namespace LanguageTests2
{
public class A { }
public class B : A {}
public class C : B {}
public static class VirtualExtensionMethods
{
private static readonly IDictionary<Type,Action<A>> _dispatchMap
= new Dictionary<Type, Action<A>>();
static VirtualExtensionMethods()
{
_dispatchMap[typeof(A)] = x => ExecuteInternal( (A)x );
_dispatchMap[typeof(B)] = x => ExecuteInternal( (B)x );
_dispatchMap[typeof(C)] = x => ExecuteInternal( (C)x );
}
public static void Execute( this A instance )
{
_dispatchMap[instance.GetType()]( instance );
}
private static void ExecuteInternal( A instance )
{
Console.WriteLine("\nCalled ToString() on: " + instance);
}
private static void ExecuteInternal(B instance)
{
Console.WriteLine( "\nCalled ToString() on: " + instance );
}
private static void ExecuteInternal(C instance)
{
Console.WriteLine("\nCalled ToString() on: " + instance);
}
}
public class VirtualExtensionsTest
{
public static void Main()
{
var instanceA = new A();
var instanceB = new B();
var instanceC = new C();
instanceA.Execute();
instanceB.Execute();
instanceC.Execute();
}
}
}
Virtual подразумевает наследование способом ООП, а методы расширения-это "просто" статические методы, которые через бит синтаксического сахара компилятор позволяет делать вид, что вызывает экземпляр типа его первого параметра. Так нет, виртуальные методы расширения не может быть и речи.
Проверьте ответ Марка Гравелла для возможного решения вашей проблемы.
можно реализовать регистр службы. Пример (серверная сторона):
static IDictionary<Type, IService> serviceRegister;
public void ServerMethod(IBusinessType object)
{
serviceRegister[obect.GetType()].Execute(object);
}
вам нужны скорее службы на вашем сервере, которые реализуют функциональность на стороне сервера, а не методы расширения. Я бы не стал вкладывать много логики в методы расширения.
позвольте мне проверить: у вас есть иерархия классов, наследуемая от A, предположительно структурированная в соответствии с вашим бизнес-доменом. Затем вы хотите добавить поведение в зависимости от того, где выполняются классы. До сих пор вы использовали методы расширения, но теперь вы не можете заставить их меняться в зависимости от иерархии классов. Какие типы поведения вы прикрепляете на сервере?
Если это такие вещи, как управление транзакциями и безопасность, политики, реализованные с помощью инъекции зависимостей à la Предложение Марка должно сработать. Вы также можете рассмотреть возможность реализации шаблон стратегии через делегатов и лямбды, для более ограниченной версии DI. Однако неясно, как клиентский код в настоящее время использует ваши классы и их методы расширения на сервере. Насколько другие классы зависят от того, как вы добавляете функциональность на стороне сервера? Являются ли они только серверными классами, которые в настоящее время ожидают найти методы расширения?
в любом случае, это звучит как будто вам понадобится тщательный дизайн тестируемости и стратегия тестирования, поскольку вы вводите вариацию по двум одновременным измерениям (иерархия наследования, среда выполнения). Надеюсь, вы используете модульное тестирование? Убедитесь, что любое выбранное вами решение (например, DI через конфигурацию) хорошо взаимодействует с тестированием и издевательством.