我有以下方法,并且我想知道是否有任何可以在下面的默认值(void),因为有一个编译器错误,说明这里的void无效:
private void applyDefaultsIfNecessary(ApplicationConfiguration configuration)
{
var defaults = new Dictionary<Predicate<ApplicationConfiguration>, Action<ApplicationConfiguration>>()
{
// { rule, action } - if rule is true, execute action
{ (c) => c.ConnectionString == null , (c) => c.ConnectionString = "foo" },
{ (c) => c.OutputExcelFilePath == null, (c) => c.ConnectionString = "bar" },
{ (c) => c.OutputDirectory == null, (c) => c.OutputDirectory = "baz" }
};
//Nothing to select, but we want to loop throough the dict and invoke action, if rule is true.
//It is a pity there is no extension method called DoForEach on collections.
defaults.Select((item) => item.Key.Invoke(configuration) ? item.Value.Invoke(configuration) : default(void) );
}
我意识到我可以使用if-else语句而不是三元运算符(或者我可以调用一个虚方法来返回void)。此外,Select扩展方法不喜欢返回void的lambda。它似乎说不能推断出类型,但当然如果我指定这样的类型,要么:
defaults.Select<ApplicationConfiguration, void>((item) => { if (item.Key.Invoke(configuration)) item.Value.Invoke(configuration); } );
我从语言设计的角度来看很奇怪,为什么我们没有可以返回void的表达式或者无效的变量的数据类型。
答案 0 :(得分:10)
我将引用您的规范7.1节,其中指出:
[表达式可能被归类为] “没有”。这发生在 表达式是对a的调用 返回类型为void的方法。 分类为空的表达式仅在a的上下文中有效 陈述表达。
[强调补充]。
也就是说唯一一次你可以使用一个表达式,这是一个返回void的方法,当表达式组成一个完整的语句时。像这样:
M();
答案 1 :(得分:9)
实际上,这违反了函数式编程规则。这有同样的缺陷Eric Lippert described about List.ForEach:你正在试图对你的收藏造成副作用。
Enumerable.Select旨在返回一个新集合 - 过滤输入。它不是为了执行代码。
话虽这么说,你可以解决这个问题:
defaults.Where(item => item.Key.Invoke(configuration)).ToList().ForEach( item => item.Value.Invoke(configuration));
这不像做的那样清晰:
var matches = defaults.Where(item => item.Key.Invoke(configuration));
foreach(var match in matches)
match.Value.Invoke(configuration);
答案 2 :(得分:3)
从语言的角度来看,void
意味着“不存在”,这就引出了一个问题:声明一个不存在的变量会有什么价值?
这里的问题不是语言限制,而是你使用需要两个右值的构造(三元运算符)的事实 - 你只有一个。
如果我可能会直言不讳,我会说你会避免if / else赞成毫无意义的简洁。你提出用来替换default(void)
的任何技巧只会在你不再烦恼这种事情之后很久就会混淆其他开发人员,或将来。
答案 3 :(得分:3)
首先,你应该避免将副作用放在标准的linq查询运算符中,其次这实际上不会起作用,因为你没有在任何地方枚举Select查询。如果你想使用linq,你可以这样做:
foreach(var item in defaults.Where(i => i.Key.Invoke(configuration)))
{
item.Value.Invoke(configuration);
}
关于你的问题,我很确定没有可能的虚空价值,你不能明确地回报它。在诸如F#的函数式语言中,void被替换为'unit',即只有一个可能值的类型 - 如果你想要你可以创建自己的单位类型并返回它。在这种情况下,你可以这样做:
defaults.Select(item => {
if(item.Key.Invoke(configuration))
{
item.Value.Invoke(configuration);
}
return Unit.Value;
}).ToList();
但我真的不能推荐这样做。
答案 4 :(得分:2)
默认不适用于void;但它适用于某种类型。 Action类没有产生结果,但是Func&lt;&gt;对象总是必须返回一个结果。无论item.Value.Invoke()返回只返回默认值,如:
默认(对象)
或者如果是特定类型:
默认(SOMETYPE)
就像那样。