null条件运算符不适用于泛型方法中的Func <t>

时间:2017-01-13 14:39:24

标签: c# null-conditional-operator null-coalescing

这是一个编译器错误还是有一个特定的选择原因,为什么空条件运算符不能与泛型方法中的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); } 然而按预期工作。

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

更新(2017-01-17):

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

假设我们有一个类(参考类型)

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

以下作品:

Func<T>

这意味着根据应用的逻辑,使用null-conditionalnull-coalescing运算符的null-conditional值类型应该有效。

然而,只要var filePath = "path/XmlFile.xml"; var xmlDoc = XDocument.Load(filePath); var parentElement = new XElement("Student"); var firstNameElement = new XElement("FirstName", firstNameVariable); var lastNameElement = new XElement("LastName", lastNameVariable); parentElement.Add(firstNameElement); parentElement.Add(lastNameElement); var rootElement = xmlDoc.Element("School"); rootElement?.Add(parentElement); xmlDoc.save(); 的左手泛型类型是通用的,没有约束,那么它就不能应用相同的逻辑,它应该能够考虑它可以将相同的逻辑应用于两个值 - 在明确应用类型时键入引用类型。

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

2 个答案:

答案 0 :(得分:7)

不幸的是,我相信你遇到了编译器的边缘情况。 ?.运算符需要返回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.
    }

答案 1 :(得分:6)

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

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

因为结构不能为空且?.需要引用类型。

由于Jeroen Mostert的评论,我看看引擎盖下发生了什么。 Func<T>是一个引用类型的委托。在T没有任何约束的情况下,代码将无法编译。 Error CS0023 Operator '?' cannot be applied to operand of type 'T'。添加约束where T: structwhere 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是类时的代码不同。所以我们修正了?错误。但是:当??是结构时,T运算符没有意义。 我认为编译器应该在此上提供编译错误。因为在结构上使用??是不允许的。 #BeMoreStrict

例如:

如果我写:

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

我收到编译错误:

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