Monday, March 28, 2011

Dynamically creating a new instance of IList's type

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

From stackoverflow
  • 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.

  • 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.

  • 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.

    Konrad Rudolph : This code doesn't work, and there's nothing similar to achieve this effect.
    AdamRalph : Oops, you are correct, have edited the post accordingly. Thanks
    : 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
    AdamRalph : 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 giving IList as an example.
  • 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.

    : The problem here is that type(s) is/are in a different assembly so i don't believe i can call Activator.CreateInstance
    Konrad Rudolph : As long as the assembly is properly loaded, this shouldn't be a problem.
    : thank you. Activator.CreateInstance works
  • 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);
        }
    
    : 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?
    bruno conde : See my updated answer...
    : Thanks Bruno, works perfectly
  • 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();
    }
    
  • 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" } );
    

0 comments:

Post a Comment