实现 IEnumerable 的结构在 LINQ 中产生意外错误

Struct implementing IEnumerable yields unexpected error with LINQ

我已经实现了一个 Maybe<T> class,它在 C# 中实现了 IEnumerable,灵感来自 Mark Seemann,它运行良好。我有一套辅助扩展方法,可以使常见操作变得可能友好,而且它们都工作得很好。我想到了将 Maybe<T> 从 class 更改为结构以防止它可以为空并且它似乎很适合解决一个问题:

var inputs = new Maybe<string>[] { /* from somewhere */ };

// This line works with class Maybe and struct Maybe
var results1 = inputs.SelectMany(x => ParseMaybe.ToInt32(x));

// This line works with class Maybe but with struct Maybe it yields a compiler error: 
// Error CS0407 'Maybe<int> ParseMaybe.ToInt32(string)' has the wrong return type
var results2 = inputs.SelectMany(ParseMaybe.ToInt32);

注意:因为 Maybe 在这里实现了 IEnumerable,所以我使用 SelectMany 而不是 Select。

此代码适用于 class 版本,但将编译器错误作为结构获取。为什么?

这是因为结构类型参数是不变的。尽管 Func<A, B> 在其 return 类型中是协变的,但这仅适用于 B 是引用类型的情况。这意味着您不能将 Func<string, Maybe<int>> 分配给 Func<string, IEnumerable<int>>,即使 Maybe<T> 实现了 IEnumerable<T>,例如

Func<string, Maybe<int>> f = ParseMaybe.ToInt32;
Func<string, IEnumerable<int>> g = f; //won't compile

相比之下,由于 int[] 是引用类型,因此以下内容将编译:

Func<string, int[]> f = ???
Func<string, IEnumerable<int>> g = f;