The compiler does not cause the corresponding general overload when passing with a value type

I have public functions:

public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class { //do something; return something; } public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct { //do something; return something; } 

Basically, I want to individually process reference types and types with a null value. It compiles; until I call the value types. For reference types, it compiles.

 mango.Get<string>(); // compiles.. mango.Get(""); // compiles.. mango.Get<int>(); // The type 'int' must be a reference type in order to use it as // parameter 'T' in the generic type or method Get<T>(Mango, T) //also // The call is ambiguous between the following methods or properties: // Get<int>(Mango, int) and Get<int>(Mango, int?) 

What is the real ambiguity here? When T is int , can it properly cause an overload of the structure? Also:

 mango.Get<int>(0); // The type 'int' must be a reference type in order to use it as // parameter 'T' in the generic type or method Get<T>(Mango, T) 

Why does the compiler only detect overload of the reference type? I tried to have two separate overloads:

 public static T Get<T>(this Mango m) where T : class { return default(T); } public static T? Get<T>(this Mango m) where T : struct { return default(T); } public static T Get<T>(this Mango m, T def) where T : class { return default(T); } public static T? Get<T>(this Mango m, T? def) where T : struct { return default(T); } 

The problem continued. And, obviously, the first two methods are not compiled here, since overloading does not work only on the basis of restrictions.

I tried to remove class limited overload and keep only struct limited, for example:

 public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct { //do something; return something; } mango.Get<int>(); // voila compiles! mango.Get<int>(0); // no problem at all.. // but now I can't have mango.Get<string>() for instance :( 

Am I left with only renaming two functions? I believe that it needs to have a unified name so that the calling user simply does not worry about implementation details, but simply calls Get for any type.

Update: Marc's solution does not work if I need to avoid an optional parameter.

 mango.Get<int>(); // still wouldnt work!! 

But there is more magic: (: (

 public static bool IsIt<T>(this T? obj) where T : struct { return who knows; } public static bool IsIt<T>(this T obj) where T : class { return perhaps; } 

By all means, I expect the same compiler error (for me) to annoy me. But this time it does not work.

 Guid? g = null; g.IsIt(); //just fine, and calls the struct constrained overload "abcd".IsIt(); //just fine, and calls the class constrained overload 

So, if overload resolution occurs before checking the restrictions, as Mark says, should I not get the same error this time? But no. Why is this so? What the hell is going on ?: X

+8
generics c # type-inference method-overloading value-type
source share
3 answers

constraint checking is performed after allowing congestion, IIRC; overload resolution seems preferable for the first version. You can force it to use another, but:

 mango.Get<int>((int?)0); 

or even:

 mango.Get((int?)0); 

Personally, I would just change the name to avoid ambiguity.

+5
source share

Interestingly, the compiler checks the constraints specified in the generic types that are used in the method signature, but not for the constraints within the signature itself.

Thus, if a method accepts two parameters, one of the types T where T : struct together with Nullable<T>[] , the compiler will not consider the method for any T that was not a structure. The specified struct method for T not taken into account when evaluating overloads, but the fact that Nullable<T> restricts T to struct is.

I really find the complete inability to consider constraints in estimating congestion strange, given that you can specify a default value of zero for the Nullable<T>[] parameter and pretend that the parameter does not exist. Vb.net compilers and C # compilers seem to be different from each other, however, when it comes to what they consider ambiguous and what they accept.

+1
source share

Let me try to answer myself.

As marc says, constraint checking is done after overload resolution and between

 public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class { //do something; return something; } 

and

 public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct { //do something; return something; } 

overload resolution prefers the class version. But this is only when the compiler is given a choice between two similar overloads ( without an optional parameter, in which case both overloads become the same, ignoring the restriction ). Now that the constraint is applied, the call fails for Get<int> , since int not a class .

Things have changed a bit when the default option is provided. If i call

 mango.Get(0); 

The compiler is capable of causing the correct overload enough, but which overload now accepts int or T where T: struct ? Not. In this example, it is expected that the second parameter will be T? , not T The compiler does not automatically allow overloading, applying all available casts for each type of argument. This is not a mistake, but it is one minute, and all that . If I do this:

 int? i = 0; mango.Get(i); 

it works, the right overload is called. This is what happens in the second example. It works because I am providing the correct parameter.

When i call:

 Guid? g = null; g.IsIt(); 

obj is known as g and therefore T is equal to Guid . But if I call

 Guid g = Guid.NewGuid(); g.IsIt(); 

This does not work, since g now Guid , not Guid? , and the compiler does not perform automatic casting, but you need to explicitly specify the compiler.

I am fine with the fact that the compiler does not perform automatic casting, since it will be too much to calculate for each possible type, but what seems to be a C # flaw is the fact that the check constraint is not involved in overload resolution. . Even if I provide a type like mango.Get<int>() or mango.Get<int>(0) , overload resolution will not use the struct version and use default(int?) For the defaultValue argument. It looks strange.

0
source share

All Articles