Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# generics contraints propagation

Tags:

c#

generics

This example is a simplification of the real problem, but how can I get this to compile? I would expect the generics constraints to propagate.

Since T is a TClass and TClass is a class, why isnt T a class?

public class MyClass<TClass> where TClass : class 
{
    public void FuncA<Ta>() where Ta : class
    {
    }

    public void FuncB<Tb>() where Tb : TClass
    {
    }

    public void Func<T>()
        where T : TClass
    {
        FuncA<T>();
        FuncB<T>();
    }
}

EDIT:

This actually works. Eric Lippert made me think, thanks.

Since T is a TClass and TClass is a TAnotherType, T is actually TAnotherType.

public class MyClass<TClass, TAnotherType> where TClass : TAnotherType
{
    public void FuncA<Ta>() where Ta : TClass
    {
    }

    public void FuncB<Tb>() where Tb : TAnotherType
    {
    }

    public void Func<T>()
        where T : TClass
    {
        FuncA<T>();
        FuncB<T>();
    }
}
like image 636
MatteS Avatar asked Nov 03 '11 21:11

MatteS


3 Answers

Since T is a TClass and TClass is a class, why isn't T a class?

Premise 1: "Bob Smith" is a proper name.

Premise 2: "A proper name" is a three word sentence fragment.

Conclusion: Therefore "Bob Smith" is a three word sentence fragment.

That logic is obviously not correct.

Premise 1: T is a TClass

Premise 2 : TClass is a class

Conclusion: Therefore T is a class.

That logic is incorrect for the same reason.

In both cases we are using "is" to mean two completely different things in the two premises. In the first premise, "is" is used to mean "these two things have the is-a-kind-of relationship between them". In the second premise, "is" is used to mean "this one thing possesses a particular characteristic".

You can see that your logic is wrong more directly by simply substituting in for T and TClass:

Premise 1: int is a System.ValueType

('is' means 'has a subclassing relationship')

Premise 2 : System.ValueType is a class

('is' means 'has a particular property, namely, being copied by reference)

Conclusion: Therefore int is a class.

And again, we come up with an incorrect conclusion because "is" is used in two inconsistent ways.

Another example:

Premise 1: A hamburger is better than nothing.

Premise 2: Nothing is better than a good steak.

Conclusion: A hamburger is better than a good steak, by transitivity.

Finally, for more thoughts on this topic, see my recent article:

http://ericlippert.com/2011/09/19/inheritance-and-representation/

like image 54
Eric Lippert Avatar answered Nov 11 '22 04:11

Eric Lippert


The problem is that T doesn't have to be a reference type. Consider:

MyClass<IFormattable> foo = new MyClass<IFormattable>();
MyClass.Func<int>();

That would try to call FuncA<int> but int doesn't obey the : class constraint.

So basically you need to add the : class constraint to Func<T> as well:

public void Func<T>() where T : class, TClass
like image 25
Jon Skeet Avatar answered Nov 11 '22 04:11

Jon Skeet


For compiling this do;

public void Func<T>()
    where T :class, TClass
{
    FuncA<T>();
    FuncB<T>();
}

because input of FunA is just a class not special class.

like image 4
Saeed Amiri Avatar answered Nov 11 '22 03:11

Saeed Amiri