B.K.
B.K.

Reputation: 10152

Populating a list based on its element type when passed to a method

Not sure if it's possible, but I'd like to be able to populate a List<T> based on what T is. Presently, I have something like this (please forgive the generic names - it's for testing purposes):

public static class CollectionsClass
{

    List<Object1> list1 = new List<Object1>();
    List<Object2> list2 = new List<Object2>();
    List<Object3> list3 = new List<Object3>();
}

public static class ActionClass
{
    public static void PopulateCollections()
    {
        Populate(CollectionsClass.list1, 0, 10);
        Populate(CollectionsClass.list2, 20, 50);
        Populate(CollectionsClass.list3, 30, 100);
    }

    private static void Populate(dynamic list, int minLimit, int maxLimit)
    {
        var rnd = new Random();
        int rndNum = rnd.Next(minLimit, maxLimit);
        for (int i = 0; i < rndNum; i++)
        {
            if (list.GetType() == typeof(List<Object1>))
            {
                list.Add(new Object1());
            }
            else if (list.GetType() == typeof(List<Object2>))
            {
                list.Add(new Object2());
            }
            else if (list.GetType() == typeof(List<Object3>))
            {
                list.Add(new Object3());
            }
            else
            {
                // put out an error
            }
        }
    }
}

While that code works, I'd like to shrink it by doing something like:

list.Add(new list.ObjectType());

I've been messing around with reflections and getting types all day, but I just can't seem to figure this one out.

Upvotes: 1

Views: 362

Answers (6)

zmbq
zmbq

Reputation: 39013

Don't use dynamics, use generics:

static void Populate<T>(List<T> list, ...) where T: new()
{
    ... 
    for (int i=0; i<rndNum; i++)
        list.Add(new T());
}

Upvotes: 4

Markus
Markus

Reputation: 22446

You can use a generic with a new constraint to achieve this:

private static void PopulateList<T>(List<T> list, int minLimit, int maxLimit) 
    where T : new()
{
    var rnd = new Random();
    int rndNum = rnd.Next(minLimit, maxLimit);
    for (int i = 0; i < rndNum; i++)
    {
         list.Add(new T());
    }
}

The constraint is that the type T must provide a default constructor. If you want to add items to a List, you do not need the dynamic keyword as you can specify List<T> as parameter type directly.

If you cannot add a default constructor, you can also provide a creator function:

private static void PopulateList<T>(List<T> list, Func<int, T> creatorFunc, 
                                    int minLimit, int maxLimit) 
{
    var rnd = new Random();
    int rndNum = rnd.Next(minLimit, maxLimit);
    for (int i = 0; i < rndNum; i++)
    {
         list.Add(creatorFunc(i));
    }
}

You call the method like this:

var lst = new List<MyObjectType>();
PopulateList<MyObjectType>(lst, x => new MyObjectType(x), 1, 7);

In this sample, the value of i is provided to the creatorFunc that returns a new object of type MyObjectType.

Upvotes: 1

Jian Huang
Jian Huang

Reputation: 1185

Use factory to extract the creation logic, reflection to get the correct type, and Activator to get the instance.

public static class TFactory 
{ 
    public static T Getmplementation<T>()
    {            
        var typeName = typeof(T).Name;
        var type = Type.GetType(typeName);
        if (type != null)
            return Activator.CreateInstance(type) as T;
        else
            throw new NotImplementedException(typeName);
    }
}

Then,

List.Add(TFactory.GetImplmentation<'T>());

Upvotes: 1

Jeroen Vannevel
Jeroen Vannevel

Reputation: 44439

Sounds like you want a combination of generics and reflection.

First of all make it generic:

void Populate<T>(List<T> mylist)

Now you know the type of your list: it's T.

All that is left is looping and creating instances of a particular type T. For this you can use Activator.CreateInstance:

for(int i = 0; i < 5; i++){
    mylist.Add((T) Activator.CreateInstance(typeof(T)));
}

With this sample code:

void Main()
{
    Populate<Type1>(new List<Type1>());
    Populate<Type2>(new List<Type2>());
}


void Populate<T>(List<T> mylist){
    for(int i = 0; i < 5; i++){
        mylist.Add((T) Activator.CreateInstance(typeof(T)));
    }

    foreach(var item in mylist){
        Console.WriteLine (item);
    }
}



class Type1 { }
class Type2 { }
class Type3 { }

You get this output:

enter image description here

This will rely on reflection to create an instance of your object any assumes there is a public non-parameter constructor available (otherwise an exception will be thrown from the Activator).

This is not quite desirable behaviour and I realized it as soon as I saw the other answers that use the where T : new() constraint in their generic function: use this method over mine.

I'll still leave it in here for completeness though (at the very least it demonstrates a possible trap).

Upvotes: 1

Dmitry
Dmitry

Reputation: 14059

Try to use generic method:

public static class CollectionsClass
{
    public static List<Object1> list1 = new List<Object1>();
    public static List<Object2> list2 = new List<Object2>();
    public static List<Object3> list3 = new List<Object3>();
}

public static class ActionClass
{
    public static void PopulateCollections()
    {
        Populate(CollectionsClass.list1, 0, 10);
        Populate(CollectionsClass.list2, 20, 50);
        Populate(CollectionsClass.list3, 30, 100);
    }

    private static void Populate<T>(List<T> list, int minLimit, int maxLimit)
        where T : new()
    {
        var rnd = new Random();
        int rndNum = rnd.Next(minLimit, maxLimit);
        for (int i = 0; i < rndNum; i++)
        {
            list.Add(new T());
        }
    }
}

Upvotes: 2

Reed Copsey
Reed Copsey

Reputation: 564363

Since you're already using dynamic, you should be able to add method to handle this:

private static void AddToList<T>(List<T> list) where T : new()
{
    list.Add(new T());
}

Given that, you can write:

private static void Populate(dynamic list, int minLimit, int maxLimit)
{
    var rnd = new Random();
    int rndNum = rnd.Next(minLimit, maxLimit);
    for (int i = 0; i < rndNum; i++)
    {
         AddToList(list);
    }
}

Upvotes: 3

Related Questions