Как создать новые универсальные элементы для списка?

#c# #generics #.net-4.0 #refactoring

#c# #общие элементы #.net-4.0 #рефакторинг

Вопрос:

Учитывая список из возможных 5 элементов, приведенный ниже метод вернет новый список ровно из 5 элементов (добавляя больше элементов, если в исходном списке их меньше 5). Это работает нормально, но теперь мне нужно реорганизовать его, чтобы он мог обрабатывать универсальный список T (тот, который будет иметь те же свойства year и Cnt). Как я могу преобразовать этот метод для получения списка и заставить его возвращать новый список с 5 элементами?

     private static List<FiveYearComplex> CreateFiveYearTemplate(int startYear, 
        int endYear, ObjectResult<FiveYearComplex> result)
    {
        var list = new List<FiveYearComplex>(5);

        for (int year = startYear; year < endYear;   year)
        {
            list.Add(new FiveYearComplex() { Year = year, Cnt = 0 });
        }
        FiveYearComplex tmpItem;
        foreach (var item in result)
        {
            tmpItem = list.Find(w => w.Year == item.Year);
            if (tmpItem == null)
            {
                tmpItem = new FiveYearComplex() { Cnt = 0, Year = item.Year };
            }
            else
            {
                tmpItem.Cnt = item.Cnt;
            }
        }
        return list;
    }
  

Когда я пытаюсь использовать List, я в конечном итоге натыкаюсь на этот раздел:

 for (int year = startYear; year < endYear;   year)
{
    list.Add(new T() { Year = year, Cnt = 0 });
}
  

и я получаю сообщение об ошибке…

Спасибо!

Для полноты картины:

     public interface IYearTemplate
    {
        int? Year { get; set; }
        decimal? Cnt { get; set; }
    }

    private static List<T> CreateFiveYearTemplate <T> (
        int startYear, int endYear, 
        ObjectResult<FiveYearAttendanceComplex> result) 
        where T : IYearTemplate, new()
    {
        var list = new List<T>(5);

        for (int year = startYear; year < endYear;   year)
        {
            list.Add(new T() { Year = year, Cnt = 0 });
        }
        T tmpItem;
        foreach (var item in result)
        {
            tmpItem = list.Find(w => w.Year == item.Year);
            if (tmpItem == null)
            {
                tmpItem = new T() { Cnt = 0, Year = item.Year };
            }
            else
            {
                tmpItem.Cnt = item.Cnt;
            }
        }
        return list;
    }
  

Спасибо вам.

Комментарии:

1. Вы почти ответили на это <T> и просто вернули другой <T>

2. но когда я пытаюсь сделать new T() { Year = год… Я получаю сообщение об ошибке…

3. Вы же понимаете, что количество элементов в вашем списке основано на значениях startYear и endYear , а не на том факте, что вы инициализируете список емкостью 5, верно?

Ответ №1:

Вы не можете легко преобразовать свой метод для обработки универсального списка, потому что ваш метод не является универсальным. Для этого требуется, чтобы у каждого элемента в списке были свойства Cnt и Year , чтобы ваш метод был универсальным, вы должны добавить это ограничение.

 public interface IYearTemplate
{
   int Cnt {get;set;}
   int Year {get;set;}
}
  

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

 private static List<T> CreateFiveYearTemplate<T>(int startYear,
    int endYear, ObjectResult<T> result) where T: IYearTemplate, new()
{
    var list = new List<T>(5);

    for (int year = startYear; year < endYear;   year)
    {
        list.Add(new T() { Year = year, Cnt = 0 });
    }
    T tmpItem;
    foreach (var item in result)
    {
        tmpItem = list.Find(w => w.Year == item.Year);
        if (tmpItem == null)
        {
            tmpItem = new T() { Cnt = 0, Year = item.Year };
        }
        else
        {
            tmpItem.Cnt = item.Cnt;
        }
    }
    return list;
}
  

Сказав, что этот метод выглядит не очень общим, поскольку ограничения очень специфичны. Почему вы хотите сделать его универсальным?

Комментарии:

1. Ну, я удалил весь словарь домена из этого примера, чтобы упростить его, но у меня будет несколько объектов, которым нужно будет возвращать список с 5 годами и 5 значениями (cnt).

2. Я все еще не уверен в ваших требованиях — итак, вы имеете дело с разными типами списков или все они возвращаются List<FiveYearComplex> — если в последнем случае вам не нужны универсальные элементы.

3. Спасибо за ответ. Универсальные элементы так полезны! Я определяю. нужно подробнее ознакомиться с этим.

4. Я имею дело с несколькими различными типами списков, такими как: FiveYearCarComplex, FiveYearTruckComplex, FiveYearSUVComplex. Сложный относится к сложному типу, созданному в результате импорта хранимой процедуры с помощью entity framework фу

Ответ №2:

Чтобы это работало для произвольного T , у вас было два варианта:

  • добавьте where T : SomeType ограничение, где SomeType либо базовый класс, либо (что более вероятно и универсально) интерфейс, который объявляет нужные вам свойства
  • переключитесь на использование dynamic (или отражение до версии 4.0) для доступа к свойствам

Первый обеспечивает безопасность во время компиляции, но требует некоторой общности между T ; второй не требует общности, но полностью выполняется во время выполнения — без проверки статическим анализом.

Например:

 interface IRenameThis {
    int Year {get;set;}
    int Cnt {get;set;}
}
  

И добавить where T : IRenameThis в сигнатуру метода (между закрытием ) параметров и открытием { тела метода)

Комментарии:

1. Спасибо за объяснение. Я не мог заставить его работать без использования new

2. @что угодно — о, верно — вам понадобится where T : IRenameThis, new() тогда — обратите внимание на new()

Ответ №3:

 static List<T> CreateFiveYearTemplate( int startYear 
                                      ,int endYear
                                      ,ObjectResult<T> result)
      where T : FiveYearComplex, new()
{
  ...
}