【发布时间】:2021-12-18 20:14:34
【问题描述】:
我可以创建任何类型的扩展方法。 例如,一旦这种类型是 int 的 Func。
我想为函数编写扩展方法,而不是函数的返回类型。
我可以用一种 hacky 的方式做到这一点:
Func<int> getUserId = () => GetUserId("Email");
int userId = getUserId.Retry(2);
其中Retry函数是一个扩展方法,定义为:
public static T Retry<T>(this Func<T> func, int maxAttempts)
{
for (int i = 0; i < maxAttempts; i++)
{
try
{
return func();
}
catch
{
}
}
throw new Exception("Retries failed.");
}
我真正想做的是:
var userId = (() => GetUserId("Email")).Retry(2);
但编译器不会将该函数协调为 T 的 Func。
我知道包括 Roslyn 在内的静态,所以我可以这样做:
Retry(() => GetUserId("Email"), 2);
但我发现这很难阅读。我真的希望我创建的辅助函数不碍事。
还有其他模式可以给我类似的结果,例如一元表达式,或使用链接(即将 T 转换为链类型,内部有一个 T,然后我为 T 链编写扩展方法) .我使用这种方法遇到的问题是,您必须通过转换为 T 链来开始表达式,然后通过转换为 T 来结束表达式,这会导致读者的注意力从我的业务逻辑上移开。
我知道我可以在 Chain of T to T 上使用隐式强制转换,但这感觉就像在幕后施展魔法一样。
那么是否有可能在不先执行函数的情况下获得对函数的引用,几乎没有样板代码?
一天结束时,我想为任何类型的 Func/Action 写以下内容:
var settings = LoadSettingsFromDatabase().Retry(2);
【问题讨论】:
-
Fluent API 不符合您的要求?
-
您知道无法直接为 lambda 创建扩展方法,因为您已经完成并看到了错误。在这一点上,它只是归结为个人偏好将 lambda 键入为您个人喜欢的代表的方式,这只是一个意见问题。有几十种方法可以做到;你已经提供了几个。
-
@alsafoo Fluent API 使用链接模式,我知道但宁愿避免。我确实使用 Fluent API 进行输入验证,但不想将其用于业务逻辑。
-
这是不可能的,因为
(() => GetUserId("Email"))不能被隐式解析。它在使用Retry(...)时有效,因为编译器能够根据Retry的参数将其匹配到可能的类型。简而言之 - 不,这是不可能的,除非你像这样包装它:var userID = new Func<int>(() => GetUserId("Email")).Retry(2) -
@Rob 我认为这应该是一个答案
标签: c#