Null-conditional operator does not work with Func <T> inside the general method

Is this a compiler error, or is there a specific chosen reason why the null condition operator does not work with Func inside common methods?

To give an example, the following does not compile

 public static T Test<T>(Func<T> func) { return func?.Invoke() ?? default(T); } 

The error that the compiler produces is CS0023 Operator '?' cannot be applied to operand of type 'T' CS0023 Operator '?' cannot be applied to operand of type 'T'

I know that you can achieve the same as in this:

 public static T Test<T>(Func<T> func) { return func != null ? func() : default(T); } 

So why is this not allowed?

For further development, Action<T> works as expected.

 public static void Test<T>(Action<T> action, T arg) { action?.Invoke(arg); } 

Update (2017-01-17):

After a few more studies, this makes even less sense, even with the following:

Say we have a class (reference type)

 public class Foo { public int Bar { get; set; } } 

and let's say that a Func<int>

 Func<int> fun = () => 10; 

The following works:

 // This work var nullableBar = foo?.Bar; // type of nullableBar is int? var bar = nullableBar ?? default(int); // type of bar is int // And this work nullableBar = fun?.Invoke(); // ditto bar = nullableBar ?? default(int); // ditto 

Which means that in accordance with the logic used there, you should use Func<T> of the value type using the null-conditional and null-coalescing .

However, as soon as the left generic null-conditional type is generic without restrictions, then it cannot apply the same logic that it should be able to consider that it can apply the same logic to both types of values and when the types are explicitly applied .

I know the limitations of compilers, it just doesnโ€™t make sense to me why this does not allow it and why it wants the result to be different, regardless of whether it refers to the type of link or type, given that manually applying the types to the expected results.

+5
source share
2 answers

Unfortunately, I believe that you are in an extreme case of the compiler. The operator ?. should return default(RetrunTypeOfRHS) for classes and default(Nullable<RetrunTypeOfRHS>) for structures. Since you did not limit T as classes or structures, it cannot determine which one should be promoted.

The reason for Action<T> is that for the return type of the right side is void for both cases, so there is no need to decide which advertisement to do.

You will need to use the long form you specified, or have two methods with different restrictions on T

  public static T TestStruct<T>(Func<T> func) where T : struct { return func?.Invoke() ?? default(T); } public static T TestClass<T>(Func<T> func) where T : class { return func?.Invoke(); // ?? default(T); -- This part is unnecessary, ?. already // returns default(T) for classes. } 
+6
source

You must set a restriction on the general function:

 public static T Test<T>(Func<T> func) where T: class { return func?.Invoke() ?? default(T); } 

Since the structure cannot be zero, ?. requires a reference type.


Due to a comment from Jeruen Mostert, I looked at what was going on under the hood. A Func<T> is a delegate that is a reference type. Without a restriction on T code will not compile. Error CS0023 Operator '?' cannot be applied to operand of type 'T' Error CS0023 Operator '?' cannot be applied to operand of type 'T' . When you add a where T: struct or where T: class constraint, the base code will be created.

The code is written:

  public static T TestStruct<T>(Func<T> func) where T : struct { return func?.Invoke() ?? default(T); } public static T TestClass<T>(Func<T> func) where T : class { return func?.Invoke() ?? default(T); } 

Code generated and decompiled with ILSpy:

  public static T TestStruct<T>(Func<T> func) where T : struct { return (func != null) ? func.Invoke() : default(T); } public static T TestClass<T>(Func<T> func) where T : class { T arg_27_0; if ((arg_27_0 = ((func != null) ? func.Invoke() : default(T))) == null) { arg_27_0 = default(T); } return arg_27_0; } 

As you can see, the code created when T is a structure is different from when T is a class. So we fixed the error ? . BUT: Operator ?? it makes no sense when T is a structure. I think the compiler should compromise on this. Because use ?? in the structure is not allowed. #BeMoreStrict

For instance:

If I write:

 var g = new MyStruct(); var p = g ?? default(MyStruct); 

I get a compilation error:

Error CS0019 Operator '??' cannot be applied to operands of type 'MainPage.MyStruct' and 'MainPage.MyStruct'

+6
source

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


All Articles