18

Can I somehow have overloaded methods which differ only by generic type constraints?

This does not compile:

    void Foo<T>(T bar) where T : class
    {

    }

    void Foo<T>(T bar) where T : struct
    {

    }

Since these are "open" methods, the actual method should be closed/constructed/fully-defined when it's referenced elsewhere in code with a concretely-typed T, and then it would be clear which overload to call.

The obvious solution is not to overload them, but I'm wondering why this doesn't work in C#?

Additional question: If this is just a C# compiler constraint, does the IL allow such an overload?

3
  • 1
    @NikhilAgrawal: yes, I always love a Jon Skeet answer, but this is also really Eric's bread and butter and so it's always interesting to see his answers to people when they say "why doesn't C# allow this?" Commented Sep 21, 2012 at 11:55
  • 1
    @Mike: Definetely because Eric will give an insider view being principal developer on the C# compiler team. Commented Sep 21, 2012 at 11:56
  • @NikhilAgrawal: exactly! Commented Sep 21, 2012 at 11:57

4 Answers 4

11

Can I somehow have overloaded methods which differ only by generic type constraints?

No. It's not part of the method signature in terms of overloading, just like the return type isn't.

There are horrible ways of "pseudo-overloading" in some cases, but I wouldn't recommend going down that path.

For more information, you might want to read:

6
  • 1
    Your blog post linked under the horrible link is exactly what I was trying to achieve, method overload for a class, value and a nullable with the same name.
    – Boris B.
    Commented Sep 21, 2012 at 12:10
  • 1
    @BorisB., one note is that if Jon is calling it horrible please dont implement it! Commented Sep 21, 2012 at 12:12
  • The "horrible" code could be improved somewhat by defining a dummy interface with a struct constraint, and using that as a dummy parameter rather than a T?. Unlike e.g. a Drawing.Rectangle? which would take 20 bytes, the interface would only take four. As for it being "horrible", it confines the nastiness to a small helper method and in most contexts the performance hit shouldn't be too bad.
    – supercat
    Commented Jul 31, 2013 at 15:32
  • 2
    @JonSkeet: I share your distaste for passing a dummy parameter, but in cases where the semantics of the method should be the same regardless of parameter type (e.g. bool TryConvert<T>(o as Object, out T result)) having the compiler auto-select among struct-only, class-only, and universal methods would seem cleaner than requiring calling code to use different names (especially since there would be no diagnostic if a slower universal method was used where a faster one could have been inferred).
    – supercat
    Commented Jul 31, 2013 at 15:46
  • 1
    @JonSkeet At least at CLR level return type is a part of method signature. CLR supports overloading by return type only and F# uses it, if I am not mistaken. Commented Sep 15, 2018 at 9:39
5

This is not possible.

Generic constraints are not considered to be part of the method signature for purposes of overloading.

If you want to allow both value types and reference types, why constrain at all?

2
  • 2
    To avoid jumping through the hoops of if (typeof(T)... (which can/should be resolved statically btw. and optimized away). Also it's not just class/struct combo, it's also where T:Foo/where T:Bar.
    – Boris B.
    Commented Sep 21, 2012 at 12:00
  • 1
    The fact that a method is available for use with a reference type or with a value type does not imply that the same method should be used in either case. As a simple example, suppose one wanted to write a method bool TryCasting<T>(object it, out T result);. If T is a class type, the method can try it as T; if it's a value type, it as T?. If T can't be inferred either way, one may have to use Reflection to select a method, but if it can be inferred at compile doing, doing so would boost performance.
    – supercat
    Commented Jul 31, 2013 at 15:40
0

An update. In C# 7.3 generic constraints are now part of overload decision.

So, this code will compile:

class Animal { } 
class Mammal : Animal { } 
class Giraffe : Mammal { }
class Reptile : Animal { } 

static void Foo<T>(T t) where T : Reptile { }
static void Foo(Animal animal) { }
static void Main() 
{ 
    Foo(new Giraffe()); 
}
1
  • 1
    Sort of... If there are method groups that contain generic constraints that aren't satisfied, these are removed. It still doesn't allow the OP to write the code in the question though, the constraints are still not part of the signature. In other words, the code in the question still doesn't compile in C# 7.3. It works here because the method signatures differ. So while your answer is factual true, it is not a good answer to this question because the question revolves around the declaration, not the overload resolution. Commented May 23, 2018 at 13:20
-1
struct _Val_Trait<T> where T:struct { }
struct _Ref_Trait<T> where T:class { }

static void Foo<T>(T bar, _Ref_Trait<T> _ = default(_Ref_Trait<T>)) where T:class
{
    Console.WriteLine("ref");
}

static void Foo<T>(T bar, _Val_Trait<T> _ = default(_Val_Trait<T>)) where T:struct
{
    Console.WriteLine("val");
}

static void Main() 
{
    Foo(1);            // -->"val"
    Foo(DateTime.Now); // -->"val"
    Foo("");           // -->"ref"

    //but:
    //Foo(null); - error: type cannot be inferred
}
1
  • This does not answer the question that was asked, as it requires the introduction of new types, and requires methods whose signatures are in fact different. The question asked here involves methods having the same signature, but with different generic type constraints. Commented Jul 28, 2019 at 3:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.