views:

94

answers:

5

I'm trying to store types in a collection, so that i can later instantiate objects of the types in the collection. But I'm not sure how to do this the best way.

What i have so far:

List<Type> list = new List<Type>();
list.Add(typeof(MyClass));
var obj = (MyClass)Activator.CreateInstance(list[0]);

I would like to have some constrains on the Type, or better yet, just a generic type in the collection instead of an instantiated Type object. Is this possible?

A: 

first way(seems stupid but it is safe):
(create a wrapper over the List || implement IList) and check .Exists(item) in the .Add Method.

Behrooz
A: 

If you were to implement your own collection, you could use generic type constraints:

public class MyList<T> 
    where T : IMyConstraint
Ian P
+5  A: 

That's not how generic collection constraints work.

Generic constraints restrict which types are legal for instantiating a generic type. There are several different kinds of constraints, but the common ones limit a generic parameter to either inherit or be an instance of a given type, or be a type that implements a particular interface (or set of interfaces).

Type, on the other hand, is a class that describes information about a type in your application or one of it's libraries. Specific kinds of types don't inherit or extend Type - rather there is a different instance of Type for each type available. You cannot use generic constraints to control which instances of Type may be added to your collection.

Without more detail about how you want to "constrain" the information in the collection, it's hard to say what route you should take. For example, if all you want to do is ensure that only unique types (no dups) are stored, that can be achieved with a HashSet (rather than a list). But if you want something more specialized - like limiting the Type instances that can be added to some subset of types, then you will likely need to implement your own collection, and implement gaurd logic in the Add/Insert methods.

LBushkin
Thanks! Actually I want to do both, no dups and only types that inherit from a specified base class. I think the factory pattern johnny g posted below, will suit my needs.
Jon List
+1  A: 

In this particular scenario, where it seems we have a "factory" pattern, we would constrain the method invoking the activator, such as

private readonly List<Type> _supportedTypes = new List<Type> ();
public void RegisterSupportedType<T> () where T : SomeConstraintType
{
    _supportedTypes.Add (typeof (T));
}

// if we do not know the type, but somehow know an index to type
public object Create (int supportedTypeIndex)
{
    object untyped = Activator.
        CreateInstance (_supportedTypes[supportedTypeIndex]);
    return untyped;
}

// if we know instance type\subtype (eg interface) and know an index
public T Create<T> (int supportedTypeIndex)
{
    T typed = default (T);
    object untyped = Create (supportedTypeIndex);
    if (!(untyped is T))
    {
        // throw meaningful exception :)
    }
    typed = (T)(untyped);
    return typed;
}

An alternative, is to create a constrained Type

public class ConstrainedType<T> 
{
    public Type Type { get; private set; }
    public ConstrainedType (Type type)
    {
        // may have this backward, would have to fact check before
        // rolling out to prod ;)
        if (!typeof (T).IsAssignableFrom (type))
        {
            // throw meaningful exception!
        }
        Type = type;
    }
}

List<ConstrainedType<SomeTypeConstraint>> list = 
    new List<ConstrainedType<SomeTypeConstraint>> ();

// will throw meaningful exception if MyClass is not 
// SomeTypeConstraint or a sub class
list.Add (new ConstrainedType (typeof (MyClass)));

SomeTypeConstraint baseType = 
    (SomeTypeConstraint)(Activator.CreateInstance(list[0].Type));
johnny g
Thank you! I think the factory pattern might just work.
Jon List
@Jon, as an fyi, you may also wish to investigate Inversion of Control [IoC] and Dependency Injection [DI]. fancy names for relatively simple concepts, and there are already several "mature" frameworks that implement generalized factory patterns like above. eg MS's Unity 2.0, and Castle project's Castle Windsor Container.
johnny g
@johnny g, Thank you very much! Turns out Unity was exactly what I needed. I'm using it to register the types, and to instantiate new objects, while keeping track of the registered types in a list. Perfect!
Jon List
+1  A: 

Jon,

If you are using CodeContracts, you could require a number of known types. The code analysis would flag any calls to your collection with invalid types.

public class TypeCollection : List<Type>
{
    public TypeCollection()
    {
    }

    public new void Add(Type type)
    {
        Contract.Requires(type == typeof(string) || type == typeof(Stream));
        base.Add(type);
    }
}

public class TestCollection
{
    public void Test()
    {
        TypeCollection collection = new TypeCollection();
        // This gets compile time warning:
        collection.Add(typeof(int));
    }
}

But, if you know the types in advance, it might make more sense to specify them all in an enum and create a collection of valid enums for the type creation you want to support.

Scott P
I'm not using CodeContracts, maybe I should. For now, I think to factory pattern johnny posted will surfice
Jon List