List<> with unknown type

I have something like this:


public List<MyClass> GetItems
{
get{ return (List<MyClass>)GetList(); }
}
public List<MyBaseClass> GetList()
{
List<MyBaseClass> lst = new List<MyBaseClass>();
Type typeOfProperty = EvaluateType();

// List<typeOfProperty> -- does not work
//... AddItems

// return (typeOfProperty)lst; -- does not work
return lst;
}

How can I create a List with the unkown type List<typeOfProperty> is not possible Or how can I cast the list from <MyBaseClass> to List<MyClass> Because return (typeOfProperty)lst; also doesn't work.



Answer this question

List<> with unknown type

  • MA2005

    Hi,

    This is an interesting subjects.... I used to be a delphi programmer. In Delphi I should define a "type of class"-type,

    so if we use this in combination with the generic List-class you could be doing such as:

    type MyBaseClass

    type MyBaseClassClass = class of MyBaseClass

    type MyClass1 : MyBaseClass

    type MyClass2: MyBaseClass

    type MyClass3: System.Object

    List<MyBaseClassClass> myList = new List<MyBaseClassClass>();

    myList.Add(MyClass1); --> check ok

    myList.Add(MyClass2); --> check ok

    myList.Add(MyClass3); ---> check fails

    to instantiatie an instance of the class:

    myList[0].Create(); ---> instantiates MyClass1 object

    myList[1].Create(); ---> instantiates MyClass2 object

    This works much beter that the List<System.Type> because in that list we can stores all kinds of types. In the delphi

    list we can only store MyBaseClass-descendant classes. But.... its just as I read in other replies: the generic list isn't

    designed to stores type-definitions. It should only being used to stored instantiated objects..... Well... ok.... I can live with that.

    Stephan


  • Quimbo

    I'm not sure how the GetValue() method knows who calls it (for example how does it know when you call it in Roles property to return the roles of the user it does not take any parameters ! What if a business object has 2 collection of the same type ) but I would suggest looking into using a generic method:

    protected T GetValue<T>()

    {

    List<T> list = new List<T>();

    // fill the list here

    return list;
    }

    public List<Role> Roles

    {

    get

    {

    return GetValue<Role>();

    }

    }


  • ETP2

    Ok it seems i have given not enough information, here the conrete Code as I implemented it yet:

    public abstract class BusinessObject : IBusinessObject
    {
       ...
       protected object GetValue()
       {
         // IF IS LIST 
          ...
          return List GetList(); // The GetList method evaluates who has called it, what type is needed.

       }

       protected List<IBusinessObject> GetList() { ... }
    }
    public partial class Role : BusinessObject
    {
       public List<Permission> Permissions
       {
          get
          {
             List<Permission> lst = new List<Permission>();
             List<IBusinessObject> lstVal = (List<IBusinessObject>)GetValue(); //calls base class method
             foreach (IBusinessObject item in lstVal)
                lst.Add((RolePermission)item);
             return lst;
          }
       }
    }
    public partial class User: BusinessObject
    {
       public List<Role> Roles
       {
          get {
             List<Role> lst = new List<Role>();
             List<IBusinessObject> lstVal = (List<IBusinessObject>)GetValue(); //calls same base class method
             foreach(IBusinessObjectitem in lstVal)
                lst.Add((Role)item);
             return lst;
          }
       }
    }

    What I want instead is:

    public partial class User: BusinessObject
    {
       public List<Role> Roles
       {
          get {
              return List<Role>GetValue();
          }
       }
    }

    The GetValue() method knows who calls it and what the caller expects to be returned. If it notices that a List<> should be returned it processes the logic to get the needed items and returns a List of objects. These object are from the needed class. So if GetValue() is called from User.Roles the List returned is indeed a List<IBusinnesObjects> but it contains concrete items of "Role".

    My main target is that the classes User, Role, Permission ... are "silly" classes, they should only call "GetValue()". The abstract BusinessObject class implements all the logic. So that any developer should just add a Property, call GetValue() and SetValue() without the need for adding any other code. I could live with the current implementation, but its just ugly.

    Hope that clears the situation, any comments welcome.


  • Marco Shaw

    Well... TaylorMichaelL showed how you can instantiate a List<T> where T is known at runtime using reflection. If you combine his answer with using generic methods you should get what you want.

    You'll have something like

    void List<T> GetValue<T> ()

    {

    return (List<T>)GetValue();

    }

    object GetValue()

    {

    System.Type TheTypeNeeded = EvaluateCallingType();

    if ( IsGenericList )
    return instantiate the generic list using reflection;
    }
    But really... using the stack trace in a function to return different results seems like a very bad idea to me. I don't think that StackTrace was designed to do this kind of things and I'm wondering how the performace is going to be and how this code will face maintaing over time.

  • Frank722

    Thanks Mike, thats a good and acceptable alternative, but it won't solve my problem completly.

    I now have:

    Class user:

    public List<Role> Roles
    {
    get {
    return GetValue<Role>((List < IBusinessObject >) GetValue());
    }
    }
    Class BusinessObject:
    protected List<T> GetValue<T> ( List<IBusinessObject> lst )
    {
    List<T> ret = new List<T>();
    foreach( IBusinessObject obj in lst )
    ret.Add((T)obj);
    return ret;
    }

    To your question: The GetValue() method uses the stacktrace to get the MethodInfo of the calling method. So I get all the information i need: Name of the Propery and expected ReturnType. So what I would need now, is something like that:

    Class BusinessObject:
    protected object GetValue()
    {
    System.Type TheTypeNeeded = EvaluateCallingType();
    if ( IsGenericList )
    return GetValue<TheTypeNeeded>( ... );
    }
    But then I have the same problem again, how do I call the GetValue<T> with a Type I evaluate at runtime

    But the current solution I created with your help seems ok to me. So i mark this reply as answer. If someone as any further suggestions feel free to comment


  • fscarpa58

    I believe you are making a false assumption. You are assuming that if you have List<MyBaseClass> that you can cast it to List<MyClass> because MyClass derives from MyBaseClass. This is not correct. List<MyClass> is distinct from List<MyBaseClass>. They are about as related as List<string> is to List<int>. Therefore doing such a conversion is unsafe (even though it works because you told the compiler you knew what you were doing). It might in fact work for a while but one day you'll probably get a runtime error when it fails. Now if you modified List<MyClass> to derive from List<MyBaseClass> then it would work (albeit it would be odd to derive a strongly typed generic class from a strongly typed generic class).

    Honestly you shouldn't, in general, return concrete classes in this case anyway. Rather you should use the interface definitions instead. This allows you to switch to, say, a Collection and still be able to return a list when needed. Does this solve your problem Not really but it is a better design in general. The problem with the layout as you have it now is that you are assuming that anything added to the List<MyBaseClass> list is of type <list<MyClass>. However the compiler won't enforce this requirement. I could create my own derived class and store it in the list. Then when I called GetItems it would blow up.

    Now for your second problem. You are correct that you can't create a new generic class instance at runtime with a type determined at runtime. At least not directly. The compiler can't guarantee at compile time that the generic class you are using meets the constraints of the type and therefore can't generate the code. If you were to always require that your EvaluateType method return, say an interface, then you could create a generic class like you want. This would be the better route. Barring that though you are stuck with using reflection at runtime. Note however that this is a very limiting situation because you can't return the created list (what type would you use other than perhaps IList) and you have to use reflection for adding, getting and updating items. Too restrictive for my taste. I'd recommend an interface or base class for all the objects that you would store and then create the generic class off of that. Here is how you'd do it though.

    // Get the element type to use
    Type typeElement = typeof(string);

    // Get the generic type definition
    Type typeList = typeof(List<int>);
    Type typeGenericList = typeList.GetGenericTypeDefinition();

    // Create the List<type>
    Type typeElementList = typeGenericList.MakeGenericType(typeElement);

    // Create an instance (use the non-generic interface for ease of use)
    IList list = Activator.CreateInstance(typeElementList) as IList;

    // Add an item
    list.Add("Testing");
    list.Add(
    1); //Should fail

    Michael Taylor - 9/28/06


  • Exclude

    The problem is that you cannot convert a List<MyBaseClass> to a List<MyClass> (or the other way arroun). The fact that MyClass inherits from MyBaseClass does not mean that List<MyClass> inherits List<MyBaseClass>. These 2 list types are unrelated.

    You may be able to create a List of typeOfProperty using reflection but given the above I don't know what you will do with the create list. You'll get an object that needs to be casted to something, but there is nothing that you can cast it to (except something like IList but then you could have use IList/ArrayList from the first place).

    The only thing that you can do here (give the sample code presented) seems to be just using List<MyBaseClass>.

    There are ways to do better but that really depends on what you are trying to achieve with the above piece of code.


  • List<> with unknown type