C# использование перегрузок для распределения работы на основе обобщенного типа

Вопрос или проблема

У меня есть класс, который хранит 3 очереди и должен иметь возможность добавлять событие данного типа в соответствующую очередь:

class MyClass{
  Queue<Event<Person>> _personQueue;
  Queue<Event<Place>> _placeQueue;
  Queue<Event<Thing>> _thingQueue;

  public void AddEvent<T>(Event<T> evt) where T : class, INoun {
    // выполнять действия
  }
}

Есть несколько вариантов для выполнить действия. Наиболее очевидный – это проверить тип обобщенного аргумента во время выполнения

if (typeof(T) == typeof(Person)) {
  _personQueue.Enqueue((Event<Person>) evt);
} else if //...

Однако, этот вопрос рекомендует перегруженные методы. Я попытался сделать это следующим образом

public void AddEvent<T>(Event<T> evt) where T : class, INoun {
  OverloadedAddEvent(evt);
}

private void OverloadedAddEvent(Event<Person> person) { _personQueue.Enqueue(person); }
private void OverloadedAddEvent(Event<Place> place) { _placeQueue.Enqueue(place); }
private void OverloadedAddEvent(Event<Thing> thing) { _thingQueue.Enqueue(thing); }

Но это не компилируется, предположительно потому, что существует вероятность, что обобщенный аргумент <T> может быть реализацией INoun, которая не является Person, Place или Thing, и я не предоставил метод для такого случая?

Я могу заставить это компилироваться, изменив аргумент AddEvent на dynamic, но не уверен, что это то, что я хочу сделать здесь.

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

Ответ или решение

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

Проблема

Вы начали с определения класса MyClass, который содержит три очереди: _personQueue, _placeQueue и _thingQueue, предназначенные для хранения событий соответствующих типов. Метод AddEvent<T> должен принимать событие и добавлять его в соответствующую очередь, но при этом возникает необходимость избежать проверок типов if или использования dynamic.

Вы правильно заметили, что полиморфизм через перегрузку методов — это подход, который улучшает читаемость и поддержку кода. Однако проблема заключается в том, что компилятор не может разрешить перегрузки для несоответствующих типов, поскольку любой тип T, который реализует интерфейс INoun, может не быть одним из ожидаемых типов: Person, Place или Thing.

Решение

К сожалению, стандартные средства C# не позволяют задавать ограничение, при котором T должен быть только одним из заранее определенных типов. Вместо этого, вы можете определить базовый класс или использовать дополнительные механизмы, такие как switch expression или комбинацию с type pattern match.

Вариант с перегрузкой методов:

Для перегрузки методов нам нужно явно обрабатывать каждый поддерживаемый тип:

public class MyClass
{
    private Queue<Event<Person>> _personQueue = new Queue<Event<Person>>();
    private Queue<Event<Place>> _placeQueue = new Queue<Event<Place>>();
    private Queue<Event<Thing>> _thingQueue = new Queue<Event<Thing>>();

    public void AddEvent<T>(Event<T> evt) where T : class, INoun
    {
        AddEventSpecific(evt);
    }

    private void AddEventSpecific(Event<Person> person) => _personQueue.Enqueue(person);
    private void AddEventSpecific(Event<Place> place) => _placeQueue.Enqueue(place);
    private void AddEventSpecific(Event<Thing> thing) => _thingQueue.Enqueue(thing);

    // Обработка неизвестных типов
    private void AddEventSpecific<T>(Event<T> evt) where T : class, INoun
    {
        throw new InvalidOperationException("Unsupported event type.");
    }
}

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

Альтернативный метод с использованием switch expression:

Если вы хотите избежать перегрузки, можно использовать switch:

public void AddEvent<T>(Event<T> evt) where T : class, INoun
{
    switch (evt)
    {
        case Event<Person> personEvent:
            _personQueue.Enqueue(personEvent);
            break;
        case Event<Place> placeEvent:
            _placeQueue.Enqueue(placeEvent);
            break;
        case Event<Thing> thingEvent:
            _thingQueue.Enqueue(thingEvent);
            break;
        default:
            throw new InvalidOperationException("Unsupported event type.");
    }
}

Заключение

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

В заключение, применение полиморфизма и четких методов обработчиков значительно упрощает работу с типами в C#, делает вашу реализацию более безопасной и эффективной.

Оцените материал
Добавить комментарий

Капча загружается...