使用此继承的通用方法的建议

时间:2010-03-19 21:43:35

标签: c# generics reflection refactoring

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

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

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返回了一个对象,并且它需要以某种方式投射到List&lt; T&gt;。

另一个是使用switch语句直到4.0,然后使用动态语言选项。

我们欢迎任何有关如何使用此方法的替代想法。我已将代码保留得非常简短,但如果您想了解任何其他详细信息,请直接询问。

更新

switch语句最初使用的是字符串,第一遍是将它移动到Presenter中,如下所示:(在交换机上没有重构,只是在数据的位置)。

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

View实现(ASPX页面的代码隐藏)

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语句中的Type或添加枚举。

我使用策略模式玩了但是当我到达装载工厂再次返回List并意识到我没有类型时,我不得不停下来。

2 个答案:

答案 0 :(得分:2)

如果没有看到调用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();
}

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

答案 1 :(得分:0)

这样的东西?

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

要摆脱大转换语句,你要么必须进一步推动泛型。 I.E.使具有switch语句的方法采用泛型类型参数,并继续前进,直到你不能再进一步,如果你必须,调用链。当这变得太困难时,请考虑设计模式,例如抽象工厂,工厂,模板方法等......这取决于调用代码的复杂程度。

相关问题