我经常发现自己想要传递一个带有返回值但没有输入的Func
来代替Action
,例如:
Func<int> DoSomething = ...;
Task.Run(DoSomething);
我并不真的关心 DoSomething
的返回值。
但是这些类型不能合一,因此我最终会将调用进行封装。
Task.Run(() => { DoSomething(); });
有没有方法可以让这些类型统一,而不需要包装?另外,它们不统一的设计原因是否合理?
我经常发现自己想要传递一个带有返回值但没有输入的Func
来代替Action
,例如:
Func<int> DoSomething = ...;
Task.Run(DoSomething);
我并不真的关心 DoSomething
的返回值。
但是这些类型不能合一,因此我最终会将调用进行封装。
Task.Run(() => { DoSomething(); });
有没有方法可以让这些类型统一,而不需要包装?另外,它们不统一的设计原因是否合理?
你希望以下陈述为真:
如果我有一个
Func<T>
,那么我应该能够在需要Action
的地方使用它。
这将要求 Func<T>
要么是(A)可分配给 Action
,要么是(B)隐式转换为 Action
。
如果我们假设(A),那就需要 T
(可以是任何类型)可分配给 void
。
Eric Lippert 在 他的博客 中回答了这个问题:
他的答案是“不”,因为这与CLI规范不兼容。CLI规范要求返回值放在堆栈上,所以对于从方法组到委托类型的协变返回类型转换,不应该将“void”视为所有可能类型的超类型吗?
void
函数最终不会生成“pop”指令,而那些有返回值的函数则会生成“pop”指令。如果有一种“操作”方式可以包含一个在编译时未知的void
函数或返回某些内容的函数,则编译器将不知道是否生成“pop”指令。Func<T>
到Action
的隐式转换,一些原本可以编译的程序将无法编译(破坏性变化)。该程序目前可以编译,但尝试取消注释隐式转换运算符,类似于您要求的内容,它将无法编译。public class FutureAction
{
public FutureAction(FutureAction action)
{
}
//public static implicit operator FutureAction(Func<int> f)
//{
// return new FutureAction(null);
//}
public static void OverloadedMethod(Func<FutureAction, FutureAction> a)
{
}
public static void OverloadedMethod(Func<Func<int>, FutureAction> a)
{
}
public static void UserCode()
{
OverloadedMethod(a => new FutureAction(a));
}
}
Func<int>
and not Func<T>
, but it illustrates the problem.)引用自CLI标准:
II.4.6.1 委托签名兼容性
委托只能被验证地绑定到目标方法,其中:
- 目标方法的签名是可以分配给委托的签名;
...
如果满足以下所有条件,则类型为T的目标方法或委托可以分配给类型为D的委托:
- T的返回类型U和D的返回类型V,V可分配给U。
Func
作为Action
传递,为什么不使用 F# 呢?与 C# 不同,F# 是从头开始设计的函数式语言。C# 简单地携带了很多来自其不太函数式的过去的包袱。正如void
和函数接受多个参数一样:P 如果你只想简化你的代码,只需创建一个AsAction
扩展方法,你就可以了。 - LuaanTask.Run(DoSomething)
的问题在哪里?它会返回一个Task<int>
,但仍然是一个Task
。当你调用Task.Run(Action)
时,你得到的也是一个Task
。 - Sriram SakthivelSystem.Void
仅用于反射。就语法而言,C#中的void
绝对不是一种类型。语法中每个需要类型的地方都有一个特殊情况来处理void
(除非void
不允许,例如在类型参数中)。 - Joey