代码之家  ›  专栏  ›  技术社区  ›  Iain Galloway

在超类型集合上迭代时进行类型转换。选择?

  •  4
  • Iain Galloway  · 技术社区  · 15 年前

    这是我经常遇到的一个问题。让我们听听你的解决方案。我将以员工管理应用程序为例:

    我们有一些实体类,其中一些实现了一个特定的接口。

    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);
    }
    
    • 我们不能修改IEemployee接口来包含子类型的详细信息,因为我们不想用只有子类型关心的详细信息污染父类型。
    • 我们没有仅包含子类型的现有集合。
    • 我们不能使用 接见者模式 因为元素类型不稳定。另外,我们可能有一个实现IRecievesBonus和IDrinkstea的类型。它的accept方法将包含对visitor.visit(this)的不明确调用。

    我们经常被迫走这条路,因为我们 不能 修改超级类型或集合,例如在.NET中,我们可能需要通过子控件集合查找此表单上的所有按钮。我们可能需要对依赖于子类型某些方面的子类型做一些事情(例如上面示例中的奖金金额)。

    我觉得奇怪的是,没有一种“可接受”的方式来做到这一点,因为它经常出现。

    1)类型转换是否值得避免?

    2)有没有我没想到的选择?

    编辑

    P_ter t_¶r_¶k建议组成员工并将类型转换进一步推下对象树:

    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);
        }
      }
    }
    

    在这个例子中,将这些特性从雇员类型中剔除是绝对值得的——特别是因为有些经理可能喝茶,而有些经理可能不喝茶——但是我们仍然有相同的类型转换的潜在问题。

    只要我们在正确的水平上做,这是“可以”的情况吗?或者我们只是在转移问题?

    圣杯将是访客模式的一个变体,其中:

    • 可以添加元素成员而不修改所有访问者
      • 访客只应访问他们感兴趣的类型
    • 访问者可以基于接口类型访问成员
      • 元素可以实现不同访问者访问的多个接口。
    • 不涉及铸造或反射

    但我很感激这可能不切实际。

    2 回复  |  直到 15 年前
        1
  •  5
  •   Péter Török    15 年前

    public enum EmployeeProperty {
      RECEIVES_BONUS,
      DRINKS_TEA,
      ...
    }
    
    public class Employee {
      Set<EmployeeProperty> properties;
      // methods to add/remove/query properties
      ... 
    }
    

    foreach(Employee employee in employees) {
      if (employee.getProperties().contains(EmployeeProperty.RECEIVES_BONUS)) {
        PayBonus(employee);
      }
    }
    

        2
  •  0
  •   Benjamin Lindley    15 年前