Jules
Jules

Reputation: 4339

Is it possible to create a collection of generic constrained types in .net?

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?

Upvotes: 0

Views: 123

Answers (6)

supercat
supercat

Reputation: 81247

It's possible to achieve a collection of things which abide by multiple generic constraints without using Reflection, but it's pretty icky. Each item added to the collection will have to be wrapped with a class object. If the collection is supposed to hold things constrained to I1 and I2, each item will be wrapped in an instance of a generic class with types T, I1, and I2 (constrained so T:I1,I2), which implements an interface with generic types I1 and I2. The collection itself will hold references of that interface type.

If anyone is interested, I could post more details.

Upvotes: 0

Andrey Shchekin
Andrey Shchekin

Reputation: 21609

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.

Upvotes: 0

Sam Harwell
Sam Harwell

Reputation: 99979

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");
    }
}

Upvotes: 2

Damien_The_Unbeliever
Damien_The_Unbeliever

Reputation: 239764

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.

Upvotes: 2

davogones
davogones

Reputation: 7399

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.

Upvotes: 0

particle
particle

Reputation: 3350

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

Upvotes: 0

Related Questions