如何在while循环中添加一个中断来解决超载歧义?
考虑这个Reactive Extensions片断(忽略它的实用性):
return Observable.Create<string>(async observable => { while (true) { } });
这不能与Reactive Extensions 2.2.5编译(使用NuGet Rx-Main软件包)。 它失败:
错误1在以下方法或属性之间的调用是不明确的:'System.Reactive.Linq.Observable.Create <string>(System.Func <System.IObserver <string>,System.Threading.Tasks.Task <System.Action> >)'and'System.Reactive.Linq.Observable.Create <string>(System.Func <System.IObserver <string>,System.Threading.Tasks.Task>)'
但是,在while循环中的任意位置添加一个break
可修复编译错误:
return Observable.Create<string>(async observable => { while (true) { break; } });
这个问题可以在没有Reactive Extensions的情况下被复制(如果你想在没有使用Rx的情况下进行尝试的话,这个问题更容易):
class Program { static void Main(string[] args) { Observable.Create<string>(async blah => { while (true) { Console.WriteLine("foo."); break; //Remove this and the compiler will break } }); } } public class Observable { public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, Task> subscribeAsync) { throw new Exception("Impl not important."); } public static IObservable<TResult> Create<TResult>(Func<IObserver<TResult>, Task<Action>> subscribeAsync) { throw new Exception("Impl not important."); } } public interface IObserver<T> { }
忽略它的Reactive Extensions部分,为什么添加break
帮助C#编译器解决歧义? 这怎么可以用C#规范中的重载parsing规则来描述呢?
我正在使用面向4.5.1的Visual Studio 2013 Update 2。
这里最简单的就是拉出async
和lambdaexpression式,因为它强调正在发生的事情。 这两种方法都是有效的,将会被编译:
public static void Foo() { while (true) { } } public static Action Foo() { while (true) { } }
但是,对于这两种方法:
public static void Foo() { while (true) { break; } } public static Action Foo() { while (true) { break; } }
第一次编译,第二次没有。 它有一个不返回有效值的代码path。
实际上, while(true){}
(与throw new Exception();
)是一个有趣的声明,因为它是任何返回types的方法的有效体。
由于无限循环对于两种重载都是合适的候选,并且重载也不是“更好”,所以会导致模糊性错误。 非无限循环实现在重载parsing中只有一个合适的候选,所以它编译。
当然,为了使async
重新起作用,这里实际上是有关联的。 对于async
方法,他们总是返回一些东西 ,无论是Task
还是Task<T>
。 重载parsing的“更好”algorithm会优先select那些返回值超过void
委托的代理,当有一个lambda可以匹配任何一个,但是在你的情况下,两个重载都有委托返回一个值,对于async
方法返回的事实一个Task
而不是一个Task<T>
是没有返回一个值的概念等价物没有被纳入那个更好的algorithm。 正因为如此,即使两个重载都适用,非asynchronous等价也不会导致歧义错误。
当然,值得注意的是,编写一个程序来确定一个任意代码块是否会完成是一个无法解决的问题,然而,虽然编译器无法正确地评估每一个代码段是否会完成,但是在某些简单的情况下作为这一个,这个代码实际上永远不会完成。 正因为如此,编写代码的方式才会明确(对你和我)来说永远不会完成,但是编译器会尽可能地完成。
离开这个async
开始…
随着中断,lambdaexpression式的结束是可达的,因此lambda的返回types必须是void
。
没有中断,lambdaexpression式的末尾是不可达的,所以任何返回types都是有效的。 例如,这很好:
Func<string> foo = () => { while(true); };
而这不是:
Func<string> foo = () => { while(true) { break; } };
因此,如果没有break
,lambdaexpression式可以转换为具有单个参数的任何委托types。 随着break
,lambdaexpression式只能转换为具有单个参数和返回types为void
的委托types。
添加async
部分和void
成为void
或Task
, void
, Task
或Task<T>
的任何T
之前,你可以有任何返回types。 例如:
// Valid Func<Task<string>> foo = async () => { while(true); }; // Invalid (it doesn't actually return a string) Func<Task<string>> foo = async () => { while(true) { break; } }; // Valid Func<Task> foo = async () => { while(true) { break; } }; // Valid Action foo = async () => { while(true) { break; } };