Nathan Albrecht
Nathan Albrecht

Reputation: 155

Looking for a better way to initialize a large list of objects in C#

new List<BaseType>
{
    new DerivedType
    {
        x="x1",y="y1",z="z1"
    },
    new DerivedType
    {
        x="x2",y="y2",z="z2"
    },
    new DerivedType
    {
        x="x3",y="y3",z="z3"
    },
    new DerivedType
    {
        x="x4",y="y4",z="z3"
    },
    ...
}

In a static method, a list is initialized via the above method, and I was wondering if there is a better way to do this syntactically.

The same fields are being set in the same order every time, in case that makes it easier.

Upvotes: 2

Views: 4729

Answers (6)

chue x
chue x

Reputation: 18813

I would use a simple array to hold the data. Then build the derived types using this array. Note that the data values can be somewhat arbitrary (string and int types shown here). This can be optimized for the original question by just using an array of strings.

object[] data = 
{
    "x1", "y1", 4,
    "x2", "y2", 3,
    "x3", "y3", 2,
    "x4", "y4", 3
};

int countPerType = 3;
int size = data.Length;

var initMe = new List<BaseType>();

for (int idx = 0; idx < size; idx += countPerType)
{
    initMe.Add(
        new DerivedType() 
        { 
            X = (string)data[idx], 
            Y = (string)data[idx + 1], 
            Z = (int)data[idx + 2] 
        });
}

Upvotes: 0

phoog
phoog

Reputation: 43046

You can make a subclass of List<T>:

public class BaseTypeList : List<BaseType>
{
    public void Add(string x, string y, string z)
    {
        Add(new DerivedType { x = x, y = y, z = z });
    }
}

Then you can use the collection initializer syntax more succinctly:

new BaseTypeList
{
    { "x1", "y1", "z1" },
    { "x2", "y2", "z2" },
    { "x3", "y3", "z3" },
    { "x4", "y4", "z3" /* (sic) */ },
    //...
}

This works because the compiler performs separate overload resolution for each element in the collection initializer block, looking for an Add method with parameter types matching the given arguments.

If you need homogeneous derived types, it gets a little ugly, but it's possible:

public class BaseTypeList : List<BaseType>
{
    public void Add(Type t, string x, string y, string z)
    {
        Add((BaseType)Activator.CreateInstance(t, x, y, z));
    }
}

Then you would initialize the collection like this:

new BaseTypeList
{
    { typeof(DerivedType1), "x1", "y1", "z1" },
    { typeof(DerivedType1), "x2", "y2", "z2" },
    { typeof(DerivedType2), "x3", "y3", "z3" },
    { typeof(DerivedType2), "x4", "y4", "z3" /* (sic) */ },
    //...
}

Upvotes: 2

Nicholas Carey
Nicholas Carey

Reputation: 74287

I find it strange that nobody has addressed the OPs more general question, and rather have focused on the contrived special case of the example.

As has been noticed by all the heavy hitters here, for the specific case of the example, as the Perl ascetics might say, "TMTOWTDI" — There's More Than One Way To Do It. Some better (or at least more concise) than others.

In the more general case, where things aren't all monotonic sequences, I don't think there is anything much better. You're stuck with a lot of repetitive boilerplate.

C# doesn't have anything like JSON or Javascript's object initializers, or C's compound literals (introduced with C99 in 2000), all of which provide far more succinct syntaxes for this sort of thing. A Pity, if you ask me.

Here's one thought, though a bit of an ugly hack: SCG.List<T> and a lot of other SCG collections have a constructor overload accepting an IEnumerable<T>, IDictionary<TKey,TValue> or something similar from which the object under construction is populated. One could conceivably embed the data required in your assembly as an embedded resource in a format convenient for your purposes. It doesn't even have to be an embedded resource: a simple multiline string literal with one comma-delimited line per item would do the trick. Provide a simple factory class or method that uses that resource to generate a LINQy IEnumerable stream of widgets that can be handed to the List<T> constructor and Bob's-Yer-Uncle.

Like I said, a bit of an ugly hack, but it would avoid the repetitive typing.

Upvotes: 1

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236248

With NBuilder you need one or two lines of code (you can get it from NuGet):

List<BaseType> list = Builder<DerivedType>.CreateListOfSize(5).Build()
                                          .ToList<BaseType>();

It initializes all properties with values "xN" "yN" "zN" (property name + element index).

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1501053

If the values are actually "x1" etc, how about:

var list = Enumerable.Range(1, n)
                     .Select(i => new DerivedType {
                                 x = "x" + i,
                                 y = "y" + i,
                                 z = "z" + i
                             })
                     .ToList<BaseType>();

That relies on the covariance in C# 4. In C# 3 (or targeting .NET 3.5) you'd need something like:

var list = Enumerable.Range(1, n)
                     .Select(i => (BaseType) new DerivedType {
                                 x = "x" + i,
                                 y = "y" + i,
                                 z = "z" + i
                             })
                     .ToList();

Or if those were just sample values (or even if they weren't), simply adding a constructor to DerivedType to allow you to pass the three properties in one go would reduce clutter:

new List<BaseType>
{
    new DerivedType("x1", "y1", "z1"),
    new DerivedType("x2", "y2", "z2"),
    new DerivedType("x3", "y3", "z3"),
    new DerivedType("x4", "y4", "z4"),
    ...
}

Upvotes: 6

JaredPar
JaredPar

Reputation: 754893

Why not use a for loop here?

var list = new List<BaseType>(SomeValue);
for (int i = 1; i < SomeValue; i++) { 
  list.Add(new DerivedType {
    x = string.Format("x{0}", i),
    y = string.Format("y{0}", i),
    z = string.Format("z{0}", i)
  });
}

Note: I'm assuming the last init in your sample code was supposed to have "z4" instead of "z3"

Upvotes: 8

Related Questions