Вопрос или проблема
У меня есть класс, который хранит 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#, делает вашу реализацию более безопасной и эффективной.