Как сделать ссылку на имя метода в интерфейсе со строгой типизацией - PullRequest
4 голосов
/ 05 марта 2012

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

У меня есть 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;
    }

}

Ответы [ 4 ]

2 голосов
/ 05 марта 2012

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

  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);
  }
1 голос
/ 05 марта 2012

Func может сделать то, что вы ищете:

        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 голосов
/ 05 марта 2012

Использовать 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 голосов
/ 05 марта 2012

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

    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);
    }
...