jcharlesworthuk
jcharlesworthuk

Reputation: 1079

Adding generic constraints at runtime?

I'm pretty stumped with this so if anyone has any ideas. I have the generic method

public void Foo<TClass>(TClass item) where TClass : class
{ }

And I want to call this method from another generic method, but this generic method doesn't have the type constraint "where TClass : class"

public void Bar<T>(T item)
{
    this.Foo<T>(item);
} 

This doesn't work, I get the error

"The type 'T' must be a reference type in order to use it as parameter 'TClass'"

Which I understand. But my question is this - is there anything I can do with C# syntax in order to "filter" the generic type "T" to pass it to "this.Bar" if it is a class. Something like....

public void Bar<T>(T item)
{
    if (typeof(T).IsClass)
        this.Foo<T **as class**>();
} 

I realise I could use reflection to call Foo, but this just seems like cheating. Is there something I can do with C# to pass "T" on with the constraint at runtime?

Also - I can't change the constraint on the method "Bar" as it comes from an interface so the constraint has to match the constraint on the interface

Upvotes: 13

Views: 1512

Answers (4)

dcastro
dcastro

Reputation: 68640

The only way to call Foo without reflection, is to cast item to one of the types/classes in its hierarchy (after the proper IsClass check).

Obviously, there's only one type in its hierarchy that you know of a priori: Object.

public void Bar<T>(T item)
{
    if (typeof(T).IsClass)
        this.Foo((object) item);
} 

Edit :

Also, in one of the comments you said you added the class constraint to be to instantiate T. You don't need that, what you need is the new constraint.

Upvotes: 4

Tamir Vered
Tamir Vered

Reputation: 10287

if (typeof(T).IsClass)
{
    this.GetType()
        .GetMethod("Foo", System.Reflection.BindingFlags.Instance |
                          System.Reflection.BindingFlags.Public)
        .Invoke(this, new object[] { item });
}

Upvotes: 0

JaredPar
JaredPar

Reputation: 754525

Unfortunately there is no way to do this without changing Bar to have the generic constraint class or using reflection. In order to compile C# must know at compile time that T is indeed a class value. There is no way to use a dynamic test such as typeof(T).IsClass in order to satisfy this compile time constraint.

You mentioned in the question that you can't change Bar but it seems like you are willing to accept the possibility of dynamic failure. Perhaps instead change Foo to not have the constraint but instead throw an exception when T is not a class type

Upvotes: 4

Euphoric
Euphoric

Reputation: 12849

I believe there is no way to make it compile. You will have to use reflection to make the call.

Actually. You could cheat if you contain it within a class:

    public class Container<T>
    {
        public Container(T value)
        {
            Value = value;
        }

        public T Value { get; private set; }
    }

    public void Bar<T>(T item)
    {
        this.Foo<Container<T>>(new Container<T>(item));
    } 

but this adds one layer you need to call-through and makes the types less clear.

Upvotes: -1

Related Questions