Why is a class constraint not found for a generic class using an inherited type?

Resolving the issue was difficult, I hope the following code snippet clarifies the situation:

public class DemoClass<TBase> where TBase : class { public void DemoMethod<T>(T target) where T : TBase { //The following line causes a design-time error: Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'T'. WeakReference<T> demoRef = new WeakReference<T>(target); } } 

WeakReference requires a type T that satisfies the class constraint. So far so good, but ...

Why can't the compiler detect that T really works because (practically) T : TBase : class ?

+5
source share
2 answers

Why can't the compiler detect what T really does, because (practically) T: TBase: class?

Because it is simply not true. In addition to what Poke points out in the response, this is also illegal because all types of values ​​inherit from object :

  var dc = new DemoClass<object>(); dc.DemoMethod(1); //woops, just attempted to create a WeakReference<int> 

Your reasoning just falls apart when value types are involved. Frivolous? Yes, but completely legal, so the compiler has no choice and should consider your code illegal.

UPDATE

Addressing John Hana's comment below, that in the above code T is not actually int , its object and 1 placed in the box implicitly, which is absolutely wrong. Consider the following variation of DemoMethod :

 public T DemoMethod<T>(T target) where T : TBase { return target; } 

And the following code:

 var dc = new DemoClass<object>(); var i = dc.DemoMethod(1); 

i is int , its not an object . In addition, the following will be executed correctly:

 long i = dc.DemoMethod(1); 

Which also proves that T cannot be placed in an int box, because the implicit conversion will fail at runtime; you cannot unpack a value type for anything but the type itself.

And, of course, you can always explicitly specify T , which also just compiles:

 dc.DemoMethod<int>(1); 
+5
source

Checks the documentation for what T : class means:

where T : class

The type argument must be a reference type; this also applies to any class, interface, delegation, or array type.

Unfortunately, this is already being done if T is, for example, an interface. So you can build a simple example where you can see that applying T : class will not transitively work:

 public interface ITest { } public struct Test : ITest { } 

If you now create a DemoClass<ITest> , you are satisfied with the type constraint, since ITest is the "class" here. But when you call the DemoMethod<Test> method, then you do not have a reference type for T , although Test inherits ITest .

In general, these special restrictions of the general type do not comply with the rules of inheritance. That is why they are defined separately and not yet set by the type system. They exist as special syntax because the type system cannot express constants otherwise.

+3
source

Source: https://habr.com/ru/post/1269494/


All Articles