实现 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;
我已经实现了一个 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;