考虑一下这个Reactive Extensions片段(忽略它的实用性):
return Observable.Create<string>(async observable =>
{
while (true)
{
}
});
Run Code Online (Sandbox Code Playgroud)
这不能使用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> >)'和'System.Reactive.Linq.Observable.Create <string>(System.Func <System.IObserver <string>,System.Threading.Tasks.Task>)'
但是,break在while循环中添加任何位置可以修复编译错误:
return Observable.Create<string>(async observable =>
{
while (true)
{
break;
}
});
Run Code Online (Sandbox Code Playgroud)
这个问题可以在没有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>
{
}
Run Code Online (Sandbox Code Playgroud)
忽略Reactive Extensions部分,为什么添加break帮助C#编译器解决了歧义?如何用C#规范中的重载决策规则来描述?
我正在使用针对4.5.1的Visual Studio 2013 Update 2.
Ser*_*rvy 59
这里最简单的就是拉出async羊羔,因为它强调了正在发生的事情.这两种方法都有效并将编译:
public static void Foo()
{
while (true) { }
}
public static Action Foo()
{
while (true) { }
}
Run Code Online (Sandbox Code Playgroud)
但是,对于这两种方法:
public static void Foo()
{
while (true) { break; }
}
public static Action Foo()
{
while (true) { break; }
}
Run Code Online (Sandbox Code Playgroud)
第一个编译,第二个没编译.它有一个不返回有效值的代码路径.
实际上,while(true){}(与...一起throw new Exception();)是一个有趣的声明,因为它是具有任何返回类型的方法的有效主体.
由于无限循环是两个重载的合适候选者,并且过载都不是"更好",因此会导致模糊错误.非无限循环实现在重载分辨率中只有一个合适的候选者,因此它编译.
当然,要async重新发挥作用,它实际上在某种程度上与此相关.对于async方法,他们总是返回一些东西,无论是a Task还是a Task<T>.对于重载的"betterness"算法会选择返回了一个值代表void你的情况却代表时,有可能会匹配任何一个lambda,两个过载都有返回值的代表,对于事实async的方法返回一个Task代替a Task<T>不是返回值的概念等价物没有包含在该更好算法中.因此,即使两个重载都适用,非异步等效也不会导致歧义错误.
当然值得注意的是,编写一个程序来确定任意代码块是否会完成是一个着名的无法解决的问题,然而,虽然编译器无法正确评估每个代码片段是否完成,但它可以证明,在某些简单的情况下,例如就像这个,代码实际上永远不会完成.因此,有一些编写代码的方法很明显(对你我来说)永远不会完成,但编译器会把它视为可能完成的.
Jon*_*eet 25
离开async这个开始......
与休息,lambda表达式的结尾是可到达的,因此拉姆达的返回类型具有成为void.
没有中断,lambda表达式的结尾是不可达的,因此任何返回类型都是有效的.例如,这很好:
Func<string> foo = () => { while(true); };
Run Code Online (Sandbox Code Playgroud)
而这不是:
Func<string> foo = () => { while(true) { break; } };
Run Code Online (Sandbox Code Playgroud)
因此,如果没有break,lambda表达式可以转换为具有单个参数的任何委托类型.使用break,lambda表达式只能转换为具有单个参数和返回类型的委托类型void.
添加async部件并void成为void或Task,vs void,Task或之前您可以拥有任何返回类型的Task<T>任何T地方.例如:
// 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; } };
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2740 次 |
| 最近记录: |