这是一个编译器错误还是有一个特定的选择原因,为什么空条件运算符不能与泛型方法中的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-conditional
和null-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();
的左手泛型类型是通用的,没有约束,那么它就不能应用相同的逻辑,它应该能够考虑它可以将相同的逻辑应用于两个值 - 在明确应用类型时键入和引用类型。
我知道编译器的限制,它对我来说没有意义,为什么它不允许它以及为什么它希望输出不同,无论它是参考还是值类型考虑手动应用类型将产量预期结果。
答案 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: struct
或where 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'