在自定义类型上使用集合初始化器语法?

时间:2012-02-08 13:36:27

标签: c# collections collection-initializer

我有一个大的静态列表,基本上是一个查找表,所以我在代码中初始化表。

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的{​​{1}}可以用这种方式初始化。

MSDN国家:

  

集合初始值设定项允许您指定一个或多个元素   初始化实现的集合类时的初始化   IEnumerable的即可。元素初始值设定项可以是一个简单的值,即   表达式或对象初始值设定项。通过使用集合初始化程序   您不必指定多次调用Add方法   您的源代码中的类;编译器添加了调用。

这是否意味着我需要在Dictionary<>类上实现IEnumerable并返回每个参数?我的班级不是收藏班。

5 个答案:

答案 0 :(得分:14)

我认为你需要制作一个自定义集合而不是List。例如,将其称为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 }
};

我现在尝试了上面的代码,它似乎对我有用。

答案 1 :(得分:5)

快速修复:创建自己的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}                
    };

更基本的答案:

你混淆了object initializerscollection initializers。简单地说:

对象初始值设定项是一种语法技巧,在后台使用指定的值调用每个命名属性的属性集方法。

集合初始化程序是一种语法技巧,在后台:

  • 对于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);
    }
}

你永远不应该在真正的程序中这样做,但我希望检查这个例子和结果,特别是如果你通过它调试步骤,将帮助你清楚地理解初始化器并为你当前的场景选择一个好的解决方案。 p>

答案 2 :(得分:4)

您尝试在列表本身上使用集合初始值设定项,而不是您的类型:

// 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>上有四个参数,并且不存在。

您必须实现自己的集合类才能使用自定义集合初始值设定项。在您使用List<T>的同时,您仍然会遇到您所拥有的构造函数。

答案 3 :(得分:1)

添加一个简单的扩展来调用所需的构造函数,而不是使用从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));
    }
}

请注意,为了简洁起见,您需要为清楚起见而进行交易,因此使用后果自负。使用“ new ListItem”可以使您直接F12到构造函数;此扩展名没有(其他开发人员也不会很明显)。

答案 4 :(得分:0)

  

这是否意味着我需要在LookupItem类上实现IEnumerable   并返回每个参数?我的班级不是收藏班。

不,这意味着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() }
}