为什么具有可空 return 类型的 Func 不适合包含具有对象 return 类型的 Func 的字典?

Why won't a Func with a nullable return type fit into a Dictionary holding Funcs with object return types?

我正在尝试构建一个如下所示的 Dictionary

var getValueFuncs = new Dictionary<TypeID, Func<string, object>>
{
  {TypeID.BINARY, NotMine.GetBinaryValue},
  {TypeID.BOOLEAN, NotMine.GetBooleanValue},
  {TypeID.DATE, NotMine.GetDateTimeValue},
  {TypeID.DOUBLE, NotMine.GetFloat64Value},
  {TypeID.LONG, NotMine.GetInteger32Value},
  {TypeID.STRING, NotMine.GetStringValue}
};

我要放入的各种Func都有不同的return类型

public interface INotMine : ICollection, ICloneable
{
  byte[] GetBinaryValue(string propName);
  bool? GetBooleanValue(string propName);
  DateTime? GetDateTimeValue(string propName);
  double? GetFloat64Value(string propName);
  int? GetInteger32Value(string propName);
  string GetStringValue(string propName);
}

请注意 NotMine 是一个单独的 class,它继承自 INotMine,我无法更改它。

用return byte[]string 编译的程序来初始化Dictionary 的两行没有问题。 return 可空数据类型 (即 bool?DateTime?double?int? 过程的四行) 不会编译。他们每个人都报告了这样一个错误:

bool? INotMine.GetBooleanValue(string propName) 
'bool? INotMine.GetBooleanValue(string)' has the wrong return type 
Expected a method with 'object GetBooleanValue(string)' signature

鉴于所有内容都继承自 object,为什么这些行不能编译并很好地进入我的 Dictionary

编辑: 在他删除它之前,有人发布了一个答案,其中包含一个优雅的解决方法,如下所示:

    {TypeID.BINARY, s => NotMine.GetBinaryValue(s)}

非常感谢,我会使用它,但我仍然想知道为什么原始行无法编译。

考虑以下几点:

Func<Animal, Turtle> fat = whatever;
Func<Mammal, Reptile> fmr = fat;

这是合法的。 fat 可以接受任何 Animal,fmr 的调用者承诺传递一个 Mammal,它始终是一个 Animal。同样,fmr 要求函数 return 是 Reptile,而 fat 总是 return 是 Turtle,即 Reptile

这种转换称为 变体 转换。具体来说,"I require any Animal so I'll accept Mammal"称为逆变转换,"I return a Turtle so I meet your need for a Reptile"称为协变转换。

C# 仅在这些情况下支持泛型类型的协变和逆变转换:

  • 有问题的类型是委托或接口。
  • 该类型已被标记为安全的变化,编译器已确认它是安全的。
  • 变化的类型参数都是引用类型.

您满足前两个条件 -- 您的 Func<string, object> 是一个已知的委托类型,在参数中是逆变的,在 return 中是协变的。但是你不满足第三个条件。 bool? DateTime?double?int? 不是引用类型。因此,变量转换规则不适用,转换是非法的。

这将涵盖从 Func<string, bool?>Func<string, object> 的转换。但这不是你在做的;您正在从 方法组 转换而来,其中包含一个 returns bool? 的函数。这重要吗?

没有。方法组转换的规则相同。从包含 returns bool? 的方法的方法组到 Func<string, object> 的协变转换是不合法的,因为 bool? 仍然是 必须是引用类型才能使该转换合法。

无论你怎么切,你都会被卡住。您不能将 returns bool? 的方法或委托直接 转换为 returns object 的委托。

Jeroen 对该问题的评论很好地总结了其原因。 装箱转换去哪里?装箱转换是分配堆内存的代码,因此该代码必须某处,但是编译器没有地方可以放置它并且仍然在委托上保持引用标识。

正如您正确指出的那样,如果您提供自己的 lambda,那么编译器就有地方进行装箱转换;它将它粘在 lambda 的 return 之上。您为此付出的代价是 lambda 现在是一个引用 lambda 主体 的委托,而不是直接指向 GetBooleanValue,因此那里有一个很小的间接惩罚。