Как привести typeof класса T к самому классу T для выполнения метода <T>, где метод принимает лямбда-аргумент? - PullRequest
2 голосов
/ 25 марта 2019

Могу ли я использовать или «привести» тип класса, например тип класса CustomClass, для этого typeof(CustomClass), чтобы выполнить метод, который полагается на предоставление соответствующего класса CustomClass в качестве «аргумента»

Method<CustomClass>();

Как я могу достичь этого, зная typeof(CustomClass)?

Подробный вопрос:

Я занимаюсь разработкой веб-API в ASP.NET 4.5. У меня есть доступ к методу расширения, который я не могу изменить, для приложения IAppBuilder при запуске:

public static class AppExtensions
{
    public static void AddExceptionTranslation<TException>(
        this IAppBuilder app,
        HttpStatusCode statusCode,
        Func<TException, ApiErrors> messageGenerator))
        where TException : Exception;
}

Этот метод расширения используется для преобразования пользовательских исключений, которые расширяют Exception, в соответствующие коды состояния. Я использую это следующим образом:

public static void SetUpExceptionHandling(IAppBuilder app)
{
    app.AddExceptionTranslation<CustomException>(HttpStatusCode.BadRequest, e => e.ToApiErrors());
    app.AddExceptionTranslation<CustomUnauthException>(HttpStatusCode.Forbidden, e => e.ToApiErrors());
    // ...
}

Сейчас мне нужно вручную добавить запись для каждого отдельного пользовательского исключения, указав TException в каждом случае. Я уже поддерживаю структуру данных, которая содержит typeof для каждого пользовательского класса исключений, а также соответствующие коды состояния, уникальные идентификаторы и другие свойства, которые я хочу сохранить только в одном месте.

Я бы хотел использовать эту существующую структуру данных для итерации и выполнить AddExceptionTranslation<TException> для каждого пользовательского исключения, где я знаю их Type.

Возможно ли это? Если нет, было бы возможно, если бы мои структурированные данные содержали что-то еще? Конечно, я мог бы просто откусить горькое яблоко и добавить каждое новое пользовательское исключение в структуру данных и , чтобы вручную добавить запись для выполнения метода, но я действительно хотел бы автоматизировать последний .

Пожалуйста, дайте мне знать, если я смогу уточнить. Спасибо!

Редактировать

AddExceptionTranslation фактически принимает второй аргумент, лямбда-выражение, которое применяется к пользовательскому исключению:

app.AddExceptionTranslation<CustomException>(HttpStatusCode.Forbidden, e => e.ToApiErrors());

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

1 Ответ

0 голосов
/ 26 марта 2019

Что вам нужно сделать, это сделать общий метод для каждого из типов в вашей коллекции.Вот как это сделать для одного типа.Приведенный ниже код очень надуманный, но показывает, как бороться с лямбдой.

var exceptionType = typeof(CustomException);

Func<CustomException, ApiErrors> func = e => e.ToApiErrors();

typeof(Extensions)
    .GetMethod(nameof(Extensions.AddException))
    .MakeGenericMethod(exceptionType)
    .Invoke(null, new object[] { builder, HttpStatusCode.Accepted, func });

Что это делает:

Это решает вашу проблему, за исключением одной проблемы.Вам по-прежнему приходится «жестко кодировать» тип исключения для каждого исключения для Func, которое абстрагируется до вызова Invoke.Чтобы это исправить, замените

Func<CustomException, ApiErrors> func = e => e.ToApiErrors();

на

var funcType = typeof(Func<,>).MakeGenericType(exceptionType, typeof(ApiErrors));
var errorsMethod = typeof(ApiErrorsExtension).GetMethod(nameof(ApiErrorsExtension.ToApiErrors));
var func = Delegate.CreateDelegate(funcType, errorsMethod);

, который

  • создает пригодную для использования версию Func<,>
  • ToApiErrors() метод, который вы действительно пытаетесь вызвать, и
  • создает делегата, который может быть передан методу AddExceptionTranslation

, поскольку он использует отражениеэто медленно.Поскольку вы делаете это при запуске, это не должно иметь значения.Если это произойдет, есть библиотеки, которые могут кэшировать отражение и выдавать более быстрый IL-код, чтобы ускорить его, но, опять же, я не буду беспокоиться об этом.

Что вы можете сделать, это упаковать приведенный выше кодв свой собственный метод, а затем вызовите его для каждого из типов в вашей коллекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...