Приведение к объекту из универсального интерфейса возвращает ноль - PullRequest
0 голосов
/ 05 февраля 2019

У меня такая ситуация

interface INotification<T>
{
    Task Handle(T t)
}

и несколько реализаций

class Notification1 : INotification<SomeEvent>
{
    //.... 
}


class Notification2 : INotification<SomeEvent>
{
    //.... 
}

Я использую ServiceProvider's GetServices(Type), чтобы получить все реализации как таковые

async Task RunAsync<TNotification>(TNotification notification)
{
    Type notificationType = notification.GetType();

    var handlerType = typeof(INotification<>).MakeGenericType(notificationType);

    var handlers = _serviceProvider.GetServices(handlerType);

    foreach (var handler in handlers)
    {
        var instance = handler as INotification<TNotification>;

        await instance.Handle(notification);
    }
}

in handlers Я получаю обработчики, которые реализуют INotification<T> В цикле foreach я пытаюсь выполнить приведение к INotification и вызвать метод Handle.

Но приведение не удается

var instance = handler as INotification<TNotification>;

Я получаю ноль .

В чем проблема с моим кодом?

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Исходя из созданного универсального типа,

var handlerType = typeof(INotification<>).MakeGenericType(notificationType);

Я полагаю, что с генериками может возникнуть некоторая путаница.

Рассмотрите возможность использования универсального расширения GetServices<T>() для поставщика услуг, котороеупростить приведенный выше код до.

async Task RunAsync<TEvent>(TEvent @event) {
    var handlers = _serviceProvider.GetServices<INotification<TEvent>>();    
    foreach (var handler in handlers) {
        await handler.Handle(@event);
    }
}

Из-за строго типизированных обобщений это упрощает вызов обработчиков, поскольку они уже будут иметь требуемый тип

Это также может быть реорганизовано для обработки всех уведомлений асинхронно с использованием Task.WhenAll вместо последовательно как в foreach цикле

async Task RunAsync<TEvent>(TEvent @event) {
    var handlers = _serviceProvider.GetServices<INotification<TEvent>>();
    if(handlers.Any()) {
        var tasks = handlers.Select(handler => handler.Handle(@event));
        await Task.WhenAll(tasks);
    }
}
0 голосов
/ 05 февраля 2019

var instance = handler as INotification<TNotification>; выглядит очень странно.Похоже, у вас обычно есть TNotification в качестве некоторого класса уведомлений, например Notification1.Таким образом, в результате приведено значение

    handler as INotification<Notification1>

, которое действительно завершится ошибкой, так как код, похоже, создает handlerType для TNotification (Notification1 в примере), который обычно не будет Notification1 : INotificaton<Notification1> (такжевозможный - Рекурсивные универсальные типы ).

Возможно, вы хотите что-то вроде

async Task RunAsync<TNotification, TEvent>(TNotification notification)
       where TNotification : INotification<TEvent>
{
    ...
    var instance = (INotification<TEvent>)handler;
    …
}

Обратите внимание, что вы должны указать тип события (TEvent), так как отдельный класс может реализоватьмножественный интерфейс, такой как

class NightWatch :
   INotification<ArrowEvent>, INotification<GunEvent>, INotification<OwlEvent>
{}

, и нет способа выяснить, к какому типу приводиться, основываясь только на классе.

...