代码之家  ›  专栏  ›  技术社区  ›  blu

关于使用此继承的泛型方法的建议

  •  4
  • blu  · 技术社区  · 15 年前

    我们继承了一个项目,它是核心业务模型的一部分的包装器。

    有一种方法接受泛型,从成员中查找与该类型匹配的项,然后返回该类型的列表。

    public List<T> GetFoos<T>()
    {
        List<IFoo> matches = Foos.FindAll(
            f => f.GetType() == typeof(T)
        );
    
        List<T> resultList = new List<T>();
        foreach (var match in matches)
        {
            resultList.Add((T)obj);
        }
    }
    

    Foos可以将同一个对象转换为继承层次结构中的不同类,从而为不同的UI表示聚合不同的总数。GetFoos可以返回20多个不同类型的后代。

    现有代码基本上有一个大的switch语句被复制并粘贴到整个代码中。每个部分中的代码使用相应的类型调用GetFoos。

    一种想法是使用反射来传递类型,这种方法非常有效,直到我们意识到Invoke返回了一个对象,并且需要以某种方式将其强制转换到列表<T>。

    我们欢迎任何关于如何使用这种方法的其他想法。我已经留下了代码相当简短,但如果你想知道更多的细节,请问。

    switch语句最初使用的是字符串,第一个步骤是将它移动到Presenter中,方法如下:(没有对switch进行重构,只对数据的位置进行重构)。

    // called on an event handler in FooPresenter
    // view is the interface for the ASPX page injected into FooPresenter's constructor
    // wrapper is the instance of the model wrapper that has the GetFoos method
    // selectedFooName is the value of a DropDownList in the Page
    //  letting the user say how they want to see the animals
    //   its either one big group (Animal)
    //   or individual types (Tiger, Lion)
    private void LoadFoos(string selectedFooName)
    {
        switch (selectedFooName)
        {
            case "Animal":  // abstract base class for all other types
                this.view.SetData(this.wrapper.GetFoos<Animal>(); 
    
            case "Lion":
                this.view.SetData(this.wrapper.GetFoos<Lion>();
                break;
    
            case "Tiger":
                this.view.SetData(this.wrapper.GetFoos<Tiger>();    
                break;
    
            case "Bear":
                this.view.SetData(this.wrapper.GetFoos<Bear>();
                break;    
        }
    }
    

    public void SetData<T>(List<T> data)
    {
        // there is a multiview on the page that contains user controls with 
        // grid layouts for the different types
    
        // there is a control for the common type of "Animal"
        // and other controls for Tiger, Bear, etc
    
        // the controls contain a 3rd party grid of pain 
        // and the grids' binding event handlers cast the data item 
        // for the row and do some stuff with it specific to that type
    }
    

    我们的第一步是至少使用switch语句中的类型,或者添加一个枚举。

    我尝试过使用策略模式,但是当我再次回到装载工厂时,发现我没有这种类型,不得不停下来。

    2 回复  |  直到 15 年前
        1
  •  2
  •   Reed Copsey    15 年前

    很难不看到调用GetFoos()的代码。。。如果您可以展示更多描述如何调用这个的代码,我们可以建议如何重构它。

    听起来解决方案是让你的调用例程也成为一个泛型例程,这样它就可以避免在20个类型之间的“切换”,只需使用一个根据需要指定的泛型类型。然而,这可能不可行,但同样,没有代码,很难知道。。。

    也就是说,可以将GetFoos重构得更简单:

    public List<T> GetFoos<T>()
    {
        return Foos.OfType<T>().ToList();
    }
    

    编辑:正如Eric Lippert所指出的,上面的代码返回的是T类型的任何类型,但也返回T的子类。尽管这很可能是实际需要的行为,但它与原始代码不同。如果出于某种原因,您不希望这样做,您可以使用:

    public List<T> GetFoos<T>()
    {
        Type type = typeof(T);
        return Foos.Where(item => item.GetType() == type).ToList();
    }
    

    这将具有与原始代码相同的行为。

        2
  •  0
  •   Richard Anthony Hein    15 年前

    像这样?

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    
    namespace SO_2480770 {
        interface IFoo {}
        class MyBase : IFoo {}
        class Bar : MyBase {}
    
        class Program {               
            IEnumerable<IFoo> Foos { get; set; }
    
            static void Main(string[] args) {           
                List<MyBase> bases = new List<MyBase>() { new MyBase(), new MyBase() };
                List<Bar> bars = new List<Bar>() { new Bar(), new Bar() };            
                Program p = new Program();
                p.Foos = bases.Concat(bars);            
                var barsFromFoos = p.GetFoos<Bar>();
                var basesFromFoos = p.GetFoos<MyBase>();
                Debug.Assert(barsFromFoos.SequenceEqual(bars));
                Debug.Assert(basesFromFoos.SequenceEqual(bases.Concat(bars)));
                Debug.Assert(!barsFromFoos.SequenceEqual(bases));
                Console.ReadLine();
            }               
    
            public List<T> GetFoos<T>() where T : IFoo {            
                return Foos.OfType<T>().ToList();
            }
        }    
    }
    

    为了摆脱大开关语句,你要么把泛型进一步推高。一、 使包含switch语句的方法接受泛型类型参数,并继续执行,直到无法再继续执行调用链(如果必须的话)。当这变得太困难时,考虑设计模式,比如抽象工厂、工厂、模板方法等等。。。。这取决于调用代码的复杂程度。