4

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

У меня есть API, который имеет несколько вызовов, которые выполняют почти то же самое, но действуют на разные объекты ввода с использованием другого метода, но всегда образуют один и тот же интерфейс. Я хочу, чтобы вырезать фрагмент & из процессов вызова метода API, чтобы общий код делался одинаковым во всех вызовах метода. Мне удалось получить рабочее решение с использованием дженериков для входных и выходных объектов, и я делаю ссылку на имя метода, которое вызывается из строки. Я хотел бы, чтобы ссылки на методы были строго типизированы, а не на основе строк, так что переименование имени метода при повторном факторинге потенциально не оставило бы «магическую» строку имени метода, ожидающего взорваться во время выполнения.

Ниже представлена ​​очень упрощенная версия того, чего я пытаюсь достичь.

class ARequest { }; 
class AResponse { }; 
class BRequest { }; 
class BResponse { }; 

interface IWorker 
{ 
    AResponse DoA(ARequest aRequest); 
    BResponse DoB(BRequest bRequest); 
} 
class Worker : IWorker 
{ 
    public AResponse DoA(ARequest aRequest) 
    { 
     return new AResponse(); 
    } 
    public BResponse DoB(BRequest bRequest) 
    { 
     return new BResponse(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     // current concrete copy & paste implementation 
     var a1 = API.DoA(new ARequest { }); 
     var b1 = API.DoB(new BRequest { }); 
     // new generic implementation 
     var a2 = API.DoA2(new ARequest { }); 
     var b2 = API.DoB2(new BRequest { }); 
    } 
} 

static class API 
{ 
    // current concrete copy & paste implementation 
    public static AResponse DoA(ARequest aRequest) 
    { 
     // lots of common code for logging & preperation 
     var worker = GetWorker(); 
     return worker.DoA(aRequest); 
    } 
    public static BResponse DoB(BRequest bRequest) 
    { 
     // lots of common code for logging & preperation 
     var worker = GetWorker(); 
     return worker.DoB(bRequest); 
    } 
    private static IWorker GetWorker() 
    { 
     return new Worker(); 
    } 
    // new generic implementation Attempt 
    public static AResponse DoA2(ARequest aRequest) 
    { 
     return DoGen<ARequest, AResponse>(aRequest, "DoA"); // how to make references to DoA and DoB methods on the IWorker strongly typed? 
    } 
    public static BResponse DoB2(BRequest bRequest) 
    { 
     return DoGen<BRequest, BResponse>(bRequest, "DoB"); // how to make references to DoA and DoB methods on the IWorker strongly typed? 
    } 
    public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, string methodname) 
     where TRequest : class 
     where TResponse : class 
    { 
     // lots of common code for logging & preperation 
     var worker = GetWorker(); 
     var mi = worker.GetType().GetMethod(methodname); 
     var result = mi.Invoke(worker, new Object[] { requestObj }); 
     return result as TResponse; 
    } 

} 
+0

ли типы реагирования, связанные каким-либо образом? – Foo42

+0

Взгляните на это сообщение в блоге, он описывает модель, которая делает то, что вам нужно: http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92 – Steven

+0

Foo42: У них есть общие поля в конечной реализации, которые относятся к общему базовому классу. В этом примере они не связаны. –

ответ

2

«магия» строка изменения имени метода делегирования на делегатом

public static AResponse DoA2(ARequest aRequest) 
    { 
    return DoGen<ARequest, AResponse>(aRequest, worker => worker.DoA); 
    } 
    public static BResponse DoB2(BRequest bRequest) 
    { 
    return DoGen<BRequest, BResponse>(bRequest, worker => worker.DoB); 
    } 
    public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, 
     Func<IWorker, Func<TRequest, TResponse>> methodRef) 
    where TRequest : class 
    where TResponse : class 
    { 
    // lots of common code for logging & preparation 
    var worker = GetWorker(); 
    var method = methodRef(worker); 

    return method(requestObj); 
    } 
+0

Это (насколько я могу судить, так или иначе) делать именно то, что я хотел. Теперь мне просто нужно узнать, что он делает. Думаю, пришло время перестать уклоняться от функторов. –

1

Func < xRequest, xResponse> может делать то, что вы ищете:

 var a1 = new Func<ARequest, AResponse>(API.DoA); 
     var b1 = new Func<BRequest, BResponse>(API.DoB); 
     var a2 = new Func<ARequest, AResponse>(API.DoA2); 
     var b2 = new Func<BRequest, BResponse>(API.DoB2); 
     a1.Invoke(new ARequest { }); 
     b1.Invoke(new BRequest { }); 
     a2.Invoke(new ARequest { }); 
     b2.Invoke(new ARequest { }); // fails at compile time 
0

Добавить делегата для mehods:

public delegate TResponse DoXDelegate<in TRequest, out TResponse>(TRequest request); 

    public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, DoXDelegate<TRequest, TResponse> method) 
     where TRequest : class 
     where TResponse : class 
    { 
     // lots of common code for logging & preperation 
     var worker = GetWorker(); 
     /*var mi = worker.GetType().GetMethod(methodname); 
     var result = mi.Invoke(worker, new Object[] { requestObj }); 
     return result as TResponse;*/ 
     return method.Invoke(requestObj); 
    } 
+0

Я не уверен, как это называется? Не могли бы вы привести пример? –

+1

Извините, прочитав другие ответы, я думаю, что я ошибся. Придерживайтесь ответа DarkGray. – raznagul

0

Использование Func<TRequest, TResponse> так:

Редактировать: Этот пример решения можно использовать только в том случае, если не имеет значения, откуда приходит рабочий объект.

// new generic implementation Attempt 
public static AResponse DoA2(ARequest aRequest) 
{ 
    return DoGen<ARequest, AResponse>(aRequest, DoA); // how to make refreces to DoA and DoB methods strongly typed? 
} 

public static BResponse DoB2(BRequest bRequest) 
{ 
    return DoGen<BRequest, BResponse>(bRequest, DoB); // how to make refreces to DoA and DoB methods strongly typed? 
} 

public static TResponse DoGen<TRequest, TResponse>(TRequest requestObj, Func<TRequest, TResponse> func) 
    where TRequest : class 
    where TResponse : class 
{ 
    // lots of common code for logging & preperation 
    var result = func(requestObj); 
    return result as TResponse; 
} 
+0

Не могли бы вы использовать методы DoA и DoB API, а не те, что были на рабочем объекте? Желаемый эффект - вызвать методы на рабочем объекте. –

+0

Возможно, я вас неправильно понял. Я думал * API.DoA * уже * static * и уже вызывает * GetWorker() *. Поэтому не нужно вызывать второй метод * GetWorker() * в * DoGen (...) *. – brgerner

+1

Я объяснил это плохо: проблема с DoA и DoB заключается в том, что они «вырезаны и вставляются» друг в друга с изменением только имени метода для рабочего. DoA2 и DoB2 предназначены для вызова универсального метода, который выполняет все общие части в одном месте, при этом единственным дифференцированием является метод для интерфейса IWorker, который вызывается, типы входных объектов и тип выходного объекта. –

Смежные вопросы