views:

41

answers:

5

Is something like this possible?

Dim l As New List(Of T As Type Where GetType(BaseClass).IsAssignableFrom(T))

Note, my collection will be a collection of Types, not objects of type T - which I know is possible.

ETA:

The answers I've had so far are as expected - I didn't think it was possible.

What I'm trying to get my head round is why the following can resolved at compile time, but not my example:

Dim l as New List(Of T As BassClass)

Are the checks not, essentially, the same?

A: 

No it cannot be. The Type in generic need to be resolve at compile time.

affan
I've added another question to my OP. I'm feeling a bit stupid today and don't understand why it can't be resolved at compile time.
Jules
+2  A: 

You can have a runtime enforced constraint on your collection like this:

public class ConstrainedTypeCollection<TBaseType> : Collection<Type>
{
    protected override void InsertItem(int index, Type item)
    {
        if (!typeof(TBaseType).IsAssignableFrom(item))
            throw new ArgumentException("The type is incompatible.", "item");

        base.InsertItem(index, item);
    }

    protected override void SetItem(int index, Type item)
    {
        if (!typeof(TBaseType).IsAssignableFrom(item))
            throw new ArgumentException("The type is incompatible.", "item");

        base.SetItem(index, item);
    }
}

Edit: You could also do the following and get full compile-time type safety as long as you call the generic Add<T>(), Contains<T>(), and Remove<T>() methods.

public class ConstrainedTypeCollection<TBaseType> : ICollection<Type>
{
    private readonly List<Type> _collection = new List<Type>();

    public void Add<T>()
        where T : TBaseType
    {
        _collection.Add(typeof(T));
    }

    public bool Contains<T>()
        where T : TBaseType
    {
        return _collection.Contains(typeof(T));
    }

    public bool Remove<T>()
        where T : TBaseType
    {
        return _collection.Remove(typeof(T));
    }

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

    bool ICollection<Type>.IsReadOnly
    {
        get
        {
            return false;
        }
    }

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

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

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

    #region ICollection<Type> Members

    void ICollection<Type>.Add(Type item)
    {
        VerifyType(item);
        _collection.Add(item);
    }

    bool ICollection<Type>.Contains(Type item)
    {
        VerifyType(item);
        return _collection.Contains(item);
    }

    bool ICollection<Type>.Remove(Type item)
    {
        VerifyType(item);
        return _collection.Remove(item);
    }

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

    private void VerifyType(Type item)
    {
        if (!typeof(TBaseType).IsAssignableFrom(item))
            throw new ArgumentException("The type is incompatible.", "item");
    }
}
280Z28
Thanks for the example. That's what I do already, I just wondered if it was possible at compile time.
Jules
Check my edit for another option. :)
280Z28
That's fantastic. It turns out it can be done but with some rather unusual syntax! Did you just think of that or have you used it before?
Jules
Just thought of it. :)
280Z28
A: 

I don't think it's possible to have that checked statically at compile time. But you could override Add, AddRange, and this[] to check added elements at run-time.

davogones
+2  A: 

The checks aren't the same - in your first example, you're asking the compiler to call the "IsAssignableFrom" method, and then do something based on the result. In the second example, you're asking the compiler's static type checker to do some work.

In general, the compiler doesn't call arbitrary code during compilation.

And also, remember that any type constraints on generics have to be embedded in your assemblies metadata. And again, you can't put arbitrary code in a generic type constraint.

Damien_The_Unbeliever
A: 

To do it you'll need to create Type(Of TBase) class, then make a list of them. Then you can try to guarantee that Type(Of TBase) is statically correct.

But in most cases, where you need to create Type(Of TBase) from CLR Type, it can be guaranteed only dynamically.

Andrey Shchekin