如果 C# 支持 void 作为泛型参数
有些问题可能看起来无所谓,但是开开脑洞还是挺有意思的
比如,最近碰到一个问题,简化形式是这样的:
IEnumerable<int> Range(int n) {
var i = 0;
while (i <= n) {
// DoSomething1...
yield return i;
i++;
}
}
Range(n).ForEach(x => DoSomething2(x));
void DoSomething2(x) {
}
看出问题了么?
IEnumerable
接口是没有ForEach
方法的,ForEach
是List<T>
的方法,所以只能写成```csharp Enumerable.Range(0, n).ToList().ForEach(x => DoSomething(x)); ```
但是显然这样就失去了延迟执行的意义了
我们也可以尝试使用
IEnumerable
的Select
方法,变成这样:```csharp Enumerable.Range(0, n).Select(x => DoSomething2(x)); ```
当然这样也行不通,因为我们的
DoSomething2
方法是void
类型的最后只能粗暴的给
DoSomething2
包装一个返回值Enumerable.Range(0, n).Select(x => { DoSomething2(x); return true; });
如果 IEnumerable
支持 ForEach
方法就好了?
- 事实上 C# 设计者对此作了解释:“foreach” vs “ForEach”
- 总之来说,
List<T>.ForEach
是List<T>
本身的方法, Linq 不会提供有副作用的方法,它违反了 Linqside-effect-free
的设计理念 - Mmm,听起来好像很有道理 :(
当然,这个例子本身不具有太大的意义,但是——
不妨开个脑洞
如果可以用 void
作为泛型参数呢?
这样的代码不再有问题了
Enumerable.Range(0, n).Select<int, void>(x => DoSomething2(x));
也并没有违反 Linq 的设计理念(当然,取决于你怎么看了 :(
更大的好处在于:
如果把void
看作泛型的一种,可以在不增加复杂度的前提下简化一些问题
比如不需要为MyClass
和MyClass<T>
写两次相同的代码了
因为MyClass
就相当于MyClass<void>
,我们可以统一所有处理逻辑,不再需要大堆的重载了。
搜索的时候发现有很多大牛讨论过这个问题了,受益匪浅
比如 C#的设计缺陷(2):不能以 void 作为泛型参数