Предложения по работе с этим унаследованным универсальным методом - PullRequest
4 голосов
/ 20 марта 2010

Мы унаследовали проект, который является оболочкой для части основной бизнес-модели.

Есть один метод, который берет универсальный, находит элементы, соответствующие этому типу, от члена, а затем возвращает список этого типа.

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 может содержать один и тот же объект, приведенный к различным классам в иерархии наследования, чтобы по-разному агрегировать итоги для разных представлений пользовательского интерфейса. Существует более 20 различных типов потомков, которые могут быть возвращены GetFoos.

Существующий код в основном имеет большой оператор switch, скопированный и вставленный в код. Код в каждом разделе вызывает GetFoos с соответствующим ему типом.

В настоящее время мы проводим рефакторинг этого в одну консолидированную область, но пока мы делаем это, мы ищем другие способы работы с этим методом.

Одна мысль состояла в том, чтобы использовать отражение для передачи типа, и это прекрасно работало, пока мы не осознали, что Invoke вернул объект, и что его нужно как-то привести к списку .

Другой вариант - просто использовать оператор 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
}

Нашим первым проходом было как минимум использование Type в операторе switch или добавление enum.

Я поэкспериментировал с использованием паттерна стратегии, но мне пришлось остановиться, когда я вернулся на фабрику загрузки, снова вернув Список и поняв, что у меня нет этого типа.

Ответы [ 2 ]

2 голосов
/ 20 марта 2010

Трудно не увидеть код, вызывающий GetFoos () ... Если вы можете показать больше кода, описывающего, как это вызывается, мы можем предложить, как его реорганизовать.

Похоже, решение состоит в том, чтобы сделать вашу вызывающую подпрограмму также обобщенной подпрограммой, чтобы избежать «переключения» между 20 типами, просто используя один универсальный тип, указанный при необходимости. Тем не менее, это может быть неосуществимо, но опять же, без кода, это трудно понять ...

При этом вы можете реорганизовать GetFoos, чтобы он был намного проще:

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

Редактировать: Как указывает Эрик Липперт, приведенный выше код возвращает любой тип, который является типом T, но также подклассы T. Хотя это, скорее всего, поведение, которое на самом деле было бы желательным, оно отличается от исходного кода , Если по какой-то причине это нежелательно, вы можете вместо этого использовать:

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

Это будет иметь то же поведение, что и исходный код.

0 голосов
/ 20 марта 2010

Как то так?

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, вам нужно либо поднять дженерики дальше. И.Е. Сделайте так, чтобы метод с оператором switch принял параметр общего типа и продолжал работать до тех пор, пока вы не сможете продвинуться дальше, если нужно, вверх по цепочке вызовов. Когда это становится слишком сложным, подумайте о шаблонах проектирования, таких как абстрактная фабрика, фабрика, методы шаблона и т. Д. ... Это зависит от того, насколько сложен вызывающий код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...