Bobson
Bobson

Reputation: 13716

List<> of specific class types

Say that I have a series of classes:

abstract class MyClass { }
class MyFirstClass : MyClass { }
class MySecondClass : MyClass { }
class MyThirdClass : MyClass { }

I want to do something based on a configurable list of these derived class types, so I want to store the chosen class's Types in a list. I know I could create a List<Type>, but I could theoretically add any class to that list. On the other hand, a List<MyClass> would be a list of instances of these classes, rather than a list of the types themselves. I could also create an enum with one value that corresponds to each derived type, and have a factory method to create the correct one as needed, but that's at least two more places I'd have to update when I added MyFourthClass.

Is there a way to do something like new List<typeof(MyClass)>() = new[] { typeof(MyFirstClass), typeof(MyThirdClass)}? Does the very fact I'm asking this question imply a problem with my design?

Upvotes: 1

Views: 811

Answers (2)

Gregory Higley
Gregory Higley

Reputation: 16598

There's no way to do this with static, compile-time type checking. Your best bet is to go with a solution like Raul Otaño's in which you do your checks at runtime.

Why can't you do this? The reason is that C# lacks static metaclass types. A metaclass is the class of a class. In other words, the instances of a metaclass are themselves classes. If C# had metaclasses, you could say something like IList<MyClassMeta> (or perhaps the syntax would be IList<meta MyClass> and the compiler would only allow you to pass MyClass (or its subclasses) as "instances", e.g.,

IList<meta MyClass> list;
list.Add(MyClass);

I've been wanting this functionality for a long time, but I don't expect it any time soon.

Upvotes: 1

Ra&#250;l Ota&#241;o
Ra&#250;l Ota&#241;o

Reputation: 4770

What you want is a generic list of types (List<Type>) but like you said, you can insert any type there. The solution I can give you is to implement your own List of types from MyClass, for instance:

class TypeMyClassList : IList<Type>
{
    private readonly List<Type> _list = new List<Type>();

    private bool CheckType(Type type)
    {
        return type.IsSubclassOf(typeof (MyClass)) || typeof (MyClass) == type;
    }

    public IEnumerator<Type> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(Type item)
    {
        if (CheckType(item))
            _list.Add(item);
        else
            throw new InvalidOperationException("You can't add other types than derived from A");
    }

    public void Clear()
    {
        _list.Clear();
    }

    public bool Contains(Type item)
    {
        return _list.Contains(item);
    }

    public void CopyTo(Type[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }

    public bool Remove(Type item)
    {
        return _list.Remove(item);
    }

    public int Count
    {
        get { return _list.Count; }
    }

    public bool IsReadOnly { get { return false; } }

    public int IndexOf(Type item)
    {
        return _list.IndexOf(item);
    }

    public void Insert(int index, Type item)
    {
        if (!CheckType(item))
            throw new InvalidOperationException("You can't insert other types than derived from A");
        _list.Add(item);
    }

    public void RemoveAt(int index)
    {
        _list.RemoveAt(index);
    }

    public Type this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            Insert(index, value);
        }
    }
}

Then you could do thinks like this that you want:

    var typeMyClassList = new TypeMyClassList
        {
            typeof(MyClass),
            typeof(MyClassA),
            typeof(MyClassB)
        };

The bad thing is that it will allows to do this in compilance time (the error will be raised on runtime):

var typeMyClassList = new TypeMyClassList
        {
            typeof(MyClass),
            typeof(MyClassA),
            typeof(MyClassB),
            typeof(string)
        };

Upvotes: 3

Related Questions