首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在while循环中添加break如何解决重载多义性?

在while循环中添加break如何解决重载多义性?
EN

Stack Overflow用户
提问于 2014-09-18 03:12:47
回答 2查看 2.8K关注 0票数 48

考虑下面这个响应式扩展片段(忽略它的实用性):

代码语言:javascript
复制
return Observable.Create<string>(async observable =>
{
    while (true)
    {
    }
});

这不能用Reactive Extensions 2.2.5编译(使用NuGet Rx-Main包)。它会失败,错误为:

错误1以下方法或属性之间的调用不明确:'System.Reactive.Linq.Observable.Create(System.Func )‘和'System.Reactive.Linq.Observable.Create(System.Func>)’

但是,在while循环中的任何位置添加break都可以修复编译错误:

代码语言:javascript
复制
return Observable.Create<string>(async observable =>
{
    while (true)
    {
        break;
    }
});

这个问题完全可以在没有响应式扩展的情况下重现(如果你想尝试而不摆弄Rx,那就更容易了):

代码语言:javascript
复制
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>
{
}

忽略它的反应式扩展部分,为什么添加break要帮助C#编译器解决歧义?如何使用C#规范中的重载解析规则来描述这一点?

我使用的是Visual Studio 2013 Update 2,目标是4.5.1。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-09-18 03:19:44

这里最简单的方法就是去掉async和lambda,因为它强调了正在发生的事情。这两种方法都是有效的,并且可以编译:

代码语言:javascript
复制
public static void Foo()
{
    while (true) { }
}
public static Action Foo()
{
    while (true) { }
}

但是,对于这两种方法:

代码语言:javascript
复制
public static void Foo()
{
    while (true) { break; }
}
public static Action Foo()
{
    while (true) { break; }
}

第一个可以编译,第二个不能。它的代码路径没有返回有效值。

实际上,while(true){} (以及throw new Exception();)是一个有趣的语句,因为它是具有任何返回类型的方法的有效主体。

由于无限循环是两个重载的合适候选者,并且两个重载都不是“更好的”,因此它会导致歧义错误。非无限循环实现在重载解析中只有一个合适的候选者,因此它会编译。

当然,为了让async重新发挥作用,它实际上在某种程度上与此相关。对于async方法,它们总是返回一些东西,无论是Task还是Task<T>。重载解析的" betterness“算法更喜欢返回值的委托,而不是void委托,但在您的例子中,两个重载都有返回值的委托,对于返回Task而不是Task<T>async方法来说,概念上等同于不返回值的事实并没有合并到betterness算法中。因此,即使两个重载都适用,非异步等效项也不会导致歧义错误。

当然,值得注意的是,编写程序来确定任意代码块是否会完成是一个众所周知的无法解决的问题,然而,尽管编译器不能正确地评估是否每个代码片段都会完成,但它可以证明,在某些简单的情况下,例如这种情况,代码实际上永远不会完成。正因为如此,有一些方法可以编写(对你我来说)永远不会完成的代码,但编译器会将其视为可能完成的代码。

票数 59
EN

Stack Overflow用户

发布于 2014-09-18 03:21:58

从一开始就让async置身事外...

有了break,lambda表达式的末尾是可达的,因此lambda的返回类型必须为void

如果没有断点,lambda表达式的末尾是无法到达的,因此任何返回类型都是有效的。例如,这是很好的:

代码语言:javascript
复制
Func<string> foo = () => { while(true); };

而这不是:

代码语言:javascript
复制
Func<string> foo = () => { while(true) { break; } };

因此,如果没有break,λ表达式将可以转换为具有单个参数的任何委托类型。使用break,lambda表达式只能转换为具有单个参数和void返回类型的委托类型。

添加async部件,对于以前可以具有任何返回类型的任何Tvoid将变为voidTask,而不是voidTaskTask<T>。例如:

代码语言:javascript
复制
// 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; } };
票数 25
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25898541

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档