Нестрогие ограничения параметров нескольких типов интерфейса?
-
19-09-2019 - |
Вопрос
Извините меня, если это обман, но, похоже, я не смог подобрать правильную комбинацию ключевых слов, чтобы отфильтровать различные ограничения типа и общие вопросы (поскольку их очень много).
У меня есть два интерфейса - давайте назовем их Ионная линия и IOffline ( Оффлайн ).
Они тесно связаны тем, что описывают почти идентичные контракты, но одно из ключевых различий между ними заключается в контексте, в котором будут использоваться конкретные реализации.Это не совсем мои обстоятельства, но это хорошо иллюстрирует проблему.
Затем у меня есть несколько методов, которые действительно работают против конкретных разработчиков этих интерфейсов.Иногда эти методы хотят иметь дело только с одним типом, а не с другим.
Достаточно просто:
public void DoStuff<T>(string foo) where T : IOnline {}
Проблема заключается в реализации кода для методов, которые могут работать с любым типом.Я думал, что это было бы правильно, но при чтении ошибки компиляции мое ожидание, что ограничение будет интерпретировано как "разрешить использование любого типа T в общем случае здесь, если они реализуют IOnline ИЛИ IOffline", на самом деле интерпретируется как "Разрешить использование любого типа T в общем случае здесь, если они реализуют ОБА".
public void DoStuff<T>(string foo) where T : IOnline, IOffline {}
Попытка реализовать два отдельных метода с одинаковым именем, но разными ограничениями завершается неудачей, поскольку возникают очевидные проблемы с неоднозначностью - мы не перегружаем, поскольку список параметров один и тот же (поскольку желаемое поведение идентично).
Я мог бы используйте два разных названия для двух разных методов, каждый с соответствующим ограничением, но это кажется запутанным и делает другие вещи ниже по течению занозой в заднице ... выполнимыми, но не идеальными.
Я чувствую, что должно быть что-то, чего мне здесь не хватает...Я чувствую себя совершенно комфортно на земле generic, но это первый раз, когда мне приходится добиваться того, к чему я стремлюсь, и я чувствую, что просто кручу свои колесики atm.
Решение
Предоставление нескольких ограничений, как в вашем втором примере, действительно является аддитивным.В Страница MSDN, посвященная общим ограничениям есть немного об этом.
Можете ли вы сделать так, чтобы ваши два интерфейса наследовались от базового интерфейса, и ограничить методы базовым типом?
Другие советы
Возможно, это не ответ на ваш вопрос, но у меня спонтанно возникает ощущение, что вы, возможно, захотите провести рефакторинг своих интерфейсов.Из вашего вопроса:
Они тесно связаны тем, что описывают почти идентичные контракты, но одно из ключевых различий между ними заключается в контексте, в котором будут использоваться конкретные реализации.
Мой взгляд на интерфейсы заключается в том, что это контракты.Они определяют, как что-то должно смотри, не совсем так , как это должно быть веди себя прилично;это и есть задача внедрения.Сейчас у меня нет информации о вашем приложении или проблемной области, но я бы, вероятно, попытался потратить некоторое время на идентификацию идентичных частей этих интерфейсов и переместить их в единый интерфейс, сохранив различия только в виде отдельных интерфейсов.Таким образом, вы, возможно, смогли бы легче преодолевать подобные проблемы.
Я думаю, что стандартный способ в .NET сделать это - иметь один интерфейс, который содержит как ваши функции IOnline, так и IOffline, а затем некоторые свойства, указывающие, какие функции фактически реализованы в определенном классе.Вы видите этот шаблон в разных местах .NET с такими вещами, как метод Seek(), который может быть реализован, а может и не быть, и свойство CanSeek, которое вы можете протестировать.
Возможно, это не самый чистый OO-дизайн, но он работает.
Теряет часть проверки во время компиляции, но я не вижу никакого способа обойти это...Вам действительно нужно выбрать, что бы вы предпочли использовать (я предполагаю, что вы предпочтете онлайн).:
public void DoStuff<T>(string foo)
{
Type type = typeof(T);
if(type.GetInterfaces().Contains(typeof(IOnline)))
doStuffOnline<T>(foo);
else if(type.GetInterfaces().Contains(typeof(IOffline)))
doStuffOffline<T>(foo);
else
throw new Exception("T must implement either IOnline or IOffline");
}
private void doStuffOnline<T>(string foo){ // can assume T : IOnline }
private void doStuffOffline<T>(string foo){ // can assume T : IOffline }