1

I'm not sure if I'm using the right terms here. But I'm just thinking, if there is any major difference between assignment compatibility and type constraints. Let me explain it with code:

public class A {}
public class B : A {}
public class C : A {}


public static void Test(A a){}
public static void Test2<T>(T a) where T : A {}

From code above, is there any difference between Test and Test2? I can call both Test and Test2 with A or any of its derived types (B and C). But, how are they different to compiler? Does generic method somehow preserves the type such that if I call it with B it will not cast it to A, I tried experimenting with overload resolution but there seems to be no difference, please see my experiment with overload resolutions.

class Program
{
    public class A {}
    public class B : A {}
    public class C : A {}

    public static void Test(A a) { O(a); }
    public static void Test2<T>(T a) where T : A { O(a); }

    public static void O(A a) { Console.WriteLine("A"); }
    public static void O(B a) { Console.WriteLine("B"); }
    public static void O(C a) { Console.WriteLine("C"); }

    static void Main(string[] args)
    {
        Test2<A>(new A());
        Test2<B>(new B());
        Test2<C>(new C());
        Test(new A());
        Test(new B());
        Test(new C());
        Console.Read();
    }
}

Everything just prints out A. Can someone shed light on this, why would you use one over other?

Shivam
  • 2,134
  • 1
  • 18
  • 28
  • From a functional perspective, I can't think of how your two examples would differ. That said, keep in mind that you can apply zero, one or more constraints to the same generic type parameter and there are a few that don't relate it to a specific base class or interface. – jmcilhinney Sep 29 '15 at 01:44
  • In addition to the answers below, one other minor tidbit of a difference potentially worthy of note comparing the two. If `A` is an interface, and `B` or `C` are a struct, then calling the generic method with the generic parameter typed against `B` or `C` (not `A`) will avoid a boxing operation. Calling the `Test(A a)` method wit a struct `B` or `C` will result in a boxing operation. – Chris Sinclair Sep 29 '15 at 02:18

2 Answers2

2

Yes, generic preserves the type. It is easier to see if method actually simply echoes parameter:

public class A 
{
   public string Question {get;set;}
}
public class B : A 
{ 
    public string Answer {get;set;}
}

T Echo<T>(T arg) where T : A 
{
   Console.WriteLine(arg.Question); // we know arg is at least "A"
   return arg;
}

var a = new A {Question = "Q1"};
var b = new B {Question = "Q2"};

Echo(a); // result is of type A
Echo(b).Answer = "42"; // result is of type B - can use "Asnwer"
Echo(3); // Compile error 3 is not A.

On second half of you sample with trying to do dispatch by type - invocation of methods decided statically at compile time - since compiler only knows that T is at least A (but can be equal to A) it has to pick void O(A a).

If you are coming from C++ world this is significant difference between templates - unlike C++ where templates compiled for given types, C# templates are compiled separately from usage for particular types - What are the differences between Generics in C# and Java... and Templates in C++?

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
0

When you define a generic class, you can apply restrictions to the kinds of types that client code can use for type arguments when it instantiates your class. If client code tries to instantiate your class by using a type that is not allowed by a constraint, the result is a compile-time error.

Why using the Constraint types? Ans: If you want to examine an item in a generic list to determine whether it is valid or to compare it to some other item, the compiler must have some guarantee that the operator or method it has to call will be supported by any type argument that might be specified by client code. This guarantee is obtained by applying one or more constraints to your generic class definition. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class.

You can find more info here: https://msdn.microsoft.com/en-us/library/d5x73970.aspx