Использование синтаксиса инициализатора коллекции для пользовательских типов? - PullRequest
10 голосов
/ 08 февраля 2012

У меня большой статический список, который в основном является справочной таблицей, поэтому я инициализирую таблицу в коде.

private class MyClass
{
    private class LookupItem
    {
        public int Param1    { get; set; }
        public int Param2    { get; set; }
        public float Param2  { get; set; }
        public float Param4  { get; set; }
    }

    private static List<LookupItem> _lookupTable = new List<LookupItem>()
    { 
        new LookupItem() { Param1 = 1, Param2 = 2, Param3 = 3 Param4 = 4 },
        new LookupItem() { Param1 = 5, Param2 = 6, Param3 = 7 Param4 = 8 },
        //etc
    }
}

Реальное LookupItem имеет много других свойств, поэтому я добавил конструктор, чтобыдля более компактного формата инициализации:

private class MyClass
{
    private class LookupItem
    {
        public int Param1    { get; set; }
        public int Param2    { get; set; }
        public float Param2  { get; set; }
        public float Param4  { get; set; }

        public LookupItem(int param1, int param2, float param3, float param4)
        {
            Param1 = param1;
            Param2 = param2;
            Param3 = param3;
            Param4 = param4;    
        }
    }

    private static List<LookupItem> _lookupTable = new List<LookupItem>()
    { 
        new LookupItem(1, 2, 3, 4),
        new LookupItem(5, 6, 7, 8),
        //etc
    }
}

Что я действительно хотел бы сделать, так это использовать формат инициализатора коллекции для самого объекта, чтобы я мог избавиться от new LookupItem() в каждой строке.Например:

private static List<LookupItem> _lookupTable = new List<LookupItem>()
{ 
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    //etc
}

Возможно ли это?Мне нравится думать, что это потому, что KeyValuePair из Dictionary<> можно инициализировать таким образом.

MSDN Состояния:

Коллекцияинициализаторы позволяют указать один или несколько инициализаторов элементов при инициализации класса коллекции , который реализует IEnumerable .Инициализаторы элемента могут быть простым значением, выражением или инициализатором объекта.Используя инициализатор коллекции, вам не нужно указывать несколько вызовов метода Add класса в вашем исходном коде;компилятор добавляет вызовы.

Значит ли это, что мне нужно реализовать IEnumerable в моем классе LookupItem и вернуть каждый параметр?Мой класс не является классом коллекции.

Ответы [ 5 ]

14 голосов
/ 08 февраля 2012

Я думаю, вам нужно сделать собственную коллекцию вместо списка.Назовите его LookupItemTable, например.Дайте этой коллекции метод Add (int, int, float, float) и предложите ему реализовать IEnumerable.Например:

class LookupItem
{
    public int a;
    public int b;
    public float c;
    public float d;
}

class LookupItemTable : List<LookupItem>
{
    public void Add(int a, int b, float c, float d)
    {
        LookupItem item = new LookupItem();
        item.a = a;
        item.b = b;
        item.c = c;
        item.d = d;
        Add(item);
    }
}

private static LookupItemTable _lookupTable = new LookupItemTable {
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 }
};

Я уже пробовал вышеуказанный код, и он, кажется, работает для меня.

5 голосов
/ 08 февраля 2012

Быстрое исправление : создайте свой собственный тип List с перегрузкой Add, которая принимает несколько аргументов:

class LookupList : List<LookupItem> {
    public void Add(int Param1, int Param2, ... sometype ParamX) {
        this.Add(new LookupItem() { Param1 = Param1, Param2 = Param2, ... ParamX = ParamX });
    }
}

Теперь работает точно так, как вы хотите:

    private static LookupList _lookupTable = new LookupList() {                  
        {1,2,3,4},                 
        {2,7,6,3}                
    };

Более фундаментальный ответ:

Вы смешиваете инициализаторы объектов и инициализаторы коллекций .Проще говоря:

Инициализаторы объектов - это синтаксический трюк, который в фоновом режиме вызывает методы набора свойств для каждого именованного свойства с указанным значением.

Коллекцияинициализаторы - это синтаксический трюк, который на заднем плане:

  • Для типа Array: заполните массив элементами
  • Для любого другого типа, который должен реализовыватьIEnumerable: вызов метода Add для каждого набора в скобках.

То есть все есть для него.Рассмотрим, например, следующий хак:

public class Hack : IEnumerable {
    public int LuckyNumber { get; set; }
    public double Total { get; private set; }
    public void Add(string message, int operand1, double operand2, double operand3) {
        Console.WriteLine(message);
        this.Total += operand1 * operand2 - operand3;
    }
    public IEnumerator GetEnumerator() { throw new NotImplementedException(); }
}

class Program {
    static void Main(string[] args) {
        Hack h1 = new Hack() {
            { "Hello", 1, 3, 2},
            { "World", 2, 7, 2.9}
        };
        Console.WriteLine(h1.Total);
        Hack h2 = new Hack() { LuckyNumber = 42 };
        Console.WriteLine(h2.LuckyNumber);
    }
}

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

4 голосов
/ 08 февраля 2012

Вы пытаетесь использовать инициализатор коллекции в самом списке, а не в вашем типе:

// Note the "new List<...>" part - that specifies what type the collection
// initializer looks at...
private static List<LookupItem> _lookupTable = new List<LookupItem>()
{ 
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
}

Итак, он ищет метод Add с четырьмя параметрами на List<T>, и этоне существует.

Вам потребуется реализовать собственный класс collection , чтобы использовать собственный инициализатор коллекции.Пока вы используете List<T>, вы застряли на вызовах конструктора, которые у вас есть.

1 голос
/ 02 января 2019

Вместо того, чтобы скрывать намерение List с помощью пользовательского класса, производного от List , добавьте простое расширение, которое вызывает требуемый конструктор:

public static class LookupItemListExtensions
{
    public static void Add(this List<LookupItem> lookupItemList, int param1, int param2, float param3, float param4)
    {
        lookupItemList.Add(new LookupItem(param1, param2, param3, param4));
    }
}

Обратите внимание, что вы торгуете ясностью для краткости,так что используйте на свой страх и риск.Использование «нового ListItem» позволяет вам F12 непосредственно в конструктор;этого расширения нет (и, скорее всего, оно не будет очевидно для других разработчиков).

0 голосов
/ 08 февраля 2012

Значит ли это, что мне нужно реализовать IEnumerable в моем классе LookupItem и возвращать каждый параметр?Мой класс не является классом коллекции.

Нет, это означает, что List<LookupItem> реализует IEnumerable, поэтому вы можете написать

private static List<LookupItem> _lookupTable = new List<LookupItem>()
{ 
    new LookupItem(1, 2, 3, 4),
    new LookupItem(5, 6, 7, 8),
    //etc
}

Это также означаетчто если бы ваша LookupItem была коллекцией, которая реализовала IEnumerable, вы могли бы написать:

private static List<LookupItem> _lookupTable = new List<LookupItem>()
{ 
    new LookupItem { new Item(), new Item() },
    new LookupItem { new Item(), new Item() }
}
...