views:

2243

answers:

7

Hi, My application is processing IList's. ILists of different user defined types. I'm thinking that i can use reflection to to see what type of object the IList contains and then create a new instance of that type and subsequently add that to the IList itself?

So at any one time I might be processing

IList<Customer> l;

and I'd like to create a new instance of Customer

Customer c = new Customer(0, "None")

and then add that onto the list

l.Add(c);

Obviously doing this dynamically at run-time is the crux of the problem. Hope somebody can give me some pointers. Thanks brendan

A: 

The big problem here is: If you don't know the type, how do you know how to make a new one? Not every type in the world has a constructor that takes an int and a string.

mquander
A: 

Yes sorry i should have mentioned that the set of objects i will be processing will have a constructor that accepts an int and a string.

+2  A: 

If you can use a parameterless constructor and set the properties afterwards then you can make your method generic, something like:-

    void Process<T>(IList<T> list, int x, string y) where T : MyBase, new()
    {
        T t = new T();
        t.X = x;
        t.Y = y;
        list.Add(t);
    }

Where MyBase is the base for your classes which expose the int and string properties. You can use an interface rather than a base class if you want.

AdamRalph
This code doesn't work, and there's nothing similar to achieve this effect.
Konrad Rudolph
Oops, you are correct, have edited the post accordingly. Thanks
AdamRalph
This looks good. In terms of usage. I have an arrayList of IList's which i'm processing in a foreach. So foreach(IList l in arrayOfILists) { Process(l, 0, "None"); } isn't going to compile which being new to generics i don't see why not (?)
apologies for the lack of formatting
So that means you want to act on a non-generic IList? In your question you said that you want to act on a generic IList<T> giving IList<Customer> as an example.
AdamRalph
+1  A: 

You can use the Activator.CreateInstance method to invoke a constructor for a class via its type name (as a string) or an instance of System.Type.

Konrad Rudolph
The problem here is that type(s) is/are in a different assembly so i don't believe i can call Activator.CreateInstance
As long as the assembly is properly loaded, this shouldn't be a problem.
Konrad Rudolph
thank you. Activator.CreateInstance works
+2  A: 

Try this:

    public static void AddNewElement<T>(IList<T> l, int i, string s)
    {
        T obj = (T)Activator.CreateInstance(typeof(T), new object[] { i, s });
        l.Add(obj);
    }

Usage:

    IList<Customer> l = new List<Customer>();
    l.Add(new Customer(1,"Hi there ..."));

    AddNewElement(l, 0, "None");

(EDIT):

Try this then:

    public static void AddNewElement2(IList l, int i, string s)
    {
        if (l == null || l.Count == 0)
            throw new ArgumentNullException();
        object obj = Activator.CreateInstance(l[0].GetType(), new object[] { i, s });
        l.Add(obj);
    }
bruno conde
I think this will work but the manner in which I'm processing the lists doesn't allow for it. i.e. foreach (IList l in arrayOfPopulatingLists){ AddNewElement(l, 0, "None")}//Compiler error:Arguments for method cannot be inferred from usage.Do i need to look into calling generic methods?
See my updated answer...
bruno conde
Thanks Bruno, works perfectly
+1  A: 

I think you should change your design. You can use abstract factory pattern. Using reflection would degrade performance.

Here is code for factory.

public abstract class MyStore {
    public abstract string Name { get; }
    public abstract void AddItem(int id, string name);
}

You can consider using interface if your abstract class has no code.

Then create Customer store.

public class CustomerStore : MyStore, IEnumerable<Customer> {
    List<Customer> list = new List<Customer>();

    public override string Name { get { return "Customer Store"; } }
    public override void AddItem(int id, string name) {
        list.Add(new Customer(id, name));
    }
    public IEnumerator<Customer> GetEnumerator() {
        return list.GetEnumerator();
    }
}

Usage

foreach (MyStore store in List<MyStore>)
    store.AddItem(0, "None");

If you want to consider type of store, use

switch (store.Name) {
case "Customer Store":
    SomeMethod((CustomerStore)store);
    break;
default:
    throw new WhatEverException();
}
chaowman
+1  A: 

You could use the Type.GetGenericArguments method to return the type argument of the generic type IList<T>. Then invoke the appropriate constructor.

  Type T = l.GetType ( ).GetGenericArguments ( ) [ 0 ];
  ConstructorInfo ctor = T.GetConstructor (
    new Type [ 2 ] { typeof ( int ), typeof ( string ) } );
  System.Diagnostics.Debug.Assert ( ctor != null );
  object instance = ctor.Invoke (
    new object [ 2 ] { 0, "None" } );
baretta