Это довольно распространенная проблема, с которой я сталкиваюсь.Давайте послушаем ваши решения.В качестве примера я собираюсь использовать приложение для управления сотрудниками: -
У нас есть несколько классов сущностей, некоторые из которых реализуют определенный интерфейс.
public interface IEmployee { ... }
public interface IRecievesBonus { int Amount { get; } }
public class Manager : IEmployee, IRecievesBonus { ... }
public class Grunt : IEmployee /* This company sucks! */ { ... }
У нас естьполучил коллекцию сотрудников, которую мы можем перебрать.Нам нужно захватить все объекты, которые реализуют IRecievesBonus, и выплатить бонус.
Наивная реализация выглядит следующим образом: -
foreach(Employee employee in employees)
{
IRecievesBonus bonusReciever = employee as IRecievesBonus;
if(bonusReciever != null)
{
PayBonus(bonusReciever);
}
}
или поочередно в C #: -
foreach(IRecievesBonus bonusReciever in employees.OfType<IRecievesBonus>())
{
PayBonus(bonusReciever);
}
- Мы не можем изменить интерфейс IEmployee, включив в него детали дочернего типа, поскольку мы не хотим загрязнять супертип деталями, о которых заботится только подтип.
- У нас нет существующей коллекции только подтипа.
- Мы не можем использовать Шаблон посетителя , поскольку типы элементов не являются стабильными.Кроме того, у нас может быть тип, который реализует как IRecievesBonus, так и IDrinksTea.Его метод Accept будет содержать неоднозначный вызов visitor.Visit (this).
Часто мы вынуждены идти по этому маршруту, потому что не можем изменить супертип,кроме коллекции, например, в .NET, нам может понадобиться найти все кнопки в этой форме через коллекцию дочерних элементов управления.Возможно, нам нужно что-то сделать с дочерними типами, что зависит от некоторого аспекта дочернего типа (например, сумма бонуса в приведенном выше примере).
Мне кажется странным, что не существует "принятого" способачтобы сделать это, учитывая, как часто это происходит.
1) Стоит ли избегать преобразования типов?
2) Есть ли альтернативы, о которых я не думал?
РЕДАКТИРОВАТЬ
Петер Тёрёк предлагает составить Employee и продвинуть преобразование типов вниз по дереву объектов: -
public interface IEmployee
{
public IList<IEmployeeProperty> Properties { get; }
}
public interface IEmployeeProperty { ... }
public class DrinksTeaProperty : IEmployeeProperty
{
int Sugars { get; set; }
bool Milk { get; set; }
}
foreach (IEmployee employee in employees)
{
foreach (IEmployeeProperty property in employee.Propeties)
{
// Handle duplicate properties if you need to.
// Since this is just an example, we'll just
// let the greedy ones have two cups of tea.
DrinksTeaProperty tea = property as DrinksTeaProperty;
if (tea != null)
{
MakeTea(tea.Sugers, tea.Milk);
}
}
}
В этом примере определенно стоит выдвинуть эти чертытипа Employee - особенно потому, что некоторые менеджеры могут пить чай, а некоторые нет - но у нас все еще остается та же основная проблема преобразования типов.
Это тот случай, когда все в порядке, пока мыэто на правильном уровне?Или мы просто решаем проблему?
Святой Грааль был бы вариантом в схеме посетителя, где: -
- Вы можете добавлять элементы элемента, не изменяя всех посетителей
- Посетители должны посещать только те типы, которые им интересны при посещении
- Посетитель может посещать участника на основе типа интерфейса
- Элементы могут реализовывать несколько интерфейсов, которыепосещают разные посетители
- Не включает кастинг или рефлексию
, но я понимаю, что это, вероятно, нереально.