8

这是编译器错误还是有特定选择的原因导致空条件运算符不适Func用于泛型方法内部?

举个例子,以下内容无法编译

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

编译器产生的错误是CS0023 Operator '?' cannot be applied to operand of type 'T'

我知道你可以做到这一点,但是:

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

那么为什么不允许呢?

然而,进一步详细说明Action<T>按预期工作。

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

更新(2017-01-17):

经过一些更多的研究,它变得更没有意义了,即使有以下几点:

假设我们有一个类(引用类型)

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

假设我们有一个Func<int>

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

以下作品:

// 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

这意味着根据那里应用的逻辑,使用and运算符Func<T>的值类型应该可以工作。null-conditionalnull-coalescing

但是,一旦左侧的泛型类型null-conditional是没有约束的泛型,那么它就不能应用它应该能够考虑的相同逻辑,当类型时,它可以将相同的逻辑应用于值类型引用类型被明确应用。

我知道编译器的限制,这对我来说没有意义,为什么它不允许它以及为什么它希望输出不同,无论它是参考类型还是值类型,考虑到手动应用类型会产生预期的结果.

4

2 回答 2

12

不幸的是,我相信您遇到了编译器的边缘情况。?.运算符需要default(RetrunTypeOfRHS)为类和default(Nullable<RetrunTypeOfRHS>)结构返回。因为您没有被限制T为类或结构,所以它无法判断要提升到哪一个。

之所以Action<T>有效,是因为右侧的返回类型void适用于这两种情况,因此不需要决定要执行哪个提升。

您将需要使用您展示的长表格或有两种具有不同约束的方法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.
    }
于 2017-01-13T14:52:31.370 回答
6

您应该对泛型函数设置约束:

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

因为 struct 不能为 null 并且?.需要引用类型。


由于 Jeroen Mostert 的评论,我了解了幕后发生的事情。AFunc<T>是一个引用类型的委托。对 没有任何约束T,代码将无法编译。Error CS0023 Operator '?' cannot be applied to operand of type 'T'. 当您添加约束where T: structorwhere T: class时,将生成底层代码。

编写的代码:

    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);
    }

使用 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;
    }

如您所见,当T是结构时生成的代码与何时T是类不同。所以我们修复了?错误。但是:当是结构时,??运算符没有意义。我认为编译器应该对此给出编译错误。因为不允许在结构上使用。#BeMoreStrictT??

例如:

如果我写:

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

我得到编译错误:

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

于 2017-01-13T14:41:38.873 回答