如何添加在循环决心超载歧义休息一下? [英] How does adding a break in a while loop resolve overload ambiguity?

查看:90
本文介绍了如何添加在循环决心超载歧义休息一下?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个无扩展片段(忽略了它的实用性):

 返回Observable.Create<串>(异步观察到= GT; 
{
,而(真)
{
}
});

这不无扩展2.2.5(使用的NuGet接收,主要封装)编译。它失败:




错误1调用以下方法或属性之间暧昧:System.Reactive.Linq.Observable.Create<字符串>(System.Func< System.IObserver<串GT;,&System.Threading.Tasks.Task LT; System.Action>>)'和'System.Reactive.Linq.Observable.Create<串GT;(System.Func<系统.IObserver<串GT;,&System.Threading.Tasks.Task GT;)




然而,加入突破在while循环的任何地方修复编译错误:

 返回Observable.Create<串>(异步观察的= GT; 
{
,而(真)
{
中断;
}
});



这个问题可以不无扩展在所有被复制(如果你想尝试没有乱搞容易RX):

 类节目
{
静态无效的主要(字串[] args)
{
Observable.Create<串>(异步等等=>
{
,而(真)
{
Console.WriteLine(富);
休息; //删除此编译器会突破
}
});
}
}

公共类观测
{
公共静态的IObservable< TResult>创建< TResult>(Func键< IObserver< TResult>中任务> subscribeAsync)
{
抛出新的异常(默认地将Impl并不重要。);
}

公共静态的IObservable< TResult>创建< TResult>(Func键< IObserver< TResult>中任务<作用>> subscribeAsync)
{
抛出新的异常(默认地将Impl并不重要。);
}
}

公共接口IObserver< T>
{
}



忽略它的反应扩展部分,为什么加入破解帮助C#编译器解决歧义?这怎么能与C#规范的重载决议规则的描述?



我在使用Visual Studio 2013年更新2靶向4.5.1。


解决方案

这是最简单直接拔掉异步还有lambda表达式在这里,因为它强调这是怎么回事。这两种方法都是有效的,并编译:

 公共静态无效美孚()
{
,而(真){}
}
公共静态动作美孚()
{
,而(真){}
}

然而,这两种方法:

 公共静态无效美孚()
{
,而(真){打破; }
}
公共静态动作美孚()
{
,而(真){打破; }
}



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



其实,,而(真){} (连同抛出新的异常(); ),是因为它与任何返回类型的方法的有效身体一个有趣的语句



由于无限循环两种重载一个合适的人选,既不超载是更好,它导致多义性错误。非无限循环的实现只有在重载一个合适的人选,所以它编译。<​​/ p>

当然,把异步回到戏剧,它是这里的一种方式实际上是相关的。对于异步方法,它们都总是返回的的东西的,无论它是一个工作任务< T> 。对于重载的betterness算法会更喜欢超过无效代表返回值代表时,有你的情况是,两者都可以匹配一个lambda,但是这两个超负荷都有代表们说,返回一个值,但事实上,对于异步返回的工作代替方法任务< T> 不是返回值未纳入该betterness算法的概念等同。正因为如此,非异步相当于不会导致多义性错误,即使这两个重载都是适用的。



当然,值得注意的是编写一个程序来确定如果代码中的任意块都不会完全是一个著名的无法解决的问题,但是,当编译器不能正确地评价每一个片段是否会完成的,它可以证明,在某些简单的情况下,如这一个,该代码将在事实上从未完成。因此有编写代码的方式,将明确(你和我)永远不会完成,但该编译器会为可能治疗完成。


Consider this Reactive Extensions snippet (ignore the practicality of it):

return Observable.Create<string>(async observable =>
{
    while (true)
    {
    }
});

This does not compile with Reactive Extensions 2.2.5 (using NuGet Rx-Main package). It fails with:

Error 1 The call is ambiguous between the following methods or properties: '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>)'

However, adding a break anywhere in the while loop fixes the compilation error:

return Observable.Create<string>(async observable =>
{
    while (true)
    {
        break;
    }
});

The problem can be reproduced without Reactive Extensions at all (easier if you want to try it without fiddling with 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>
{
}

Ignoring the Reactive Extensions part of it, Why does adding break help the C# compiler resolve the ambiguity? How can this be described with the rules of overload resolution from the C# specification?

I'm using Visual Studio 2013 Update 2 targeting 4.5.1.

解决方案

It's easiest to just pull out async as well as the lambdas here, as it emphasizes what's going on. Both of these methods are valid and will compile:

public static void Foo()
{
    while (true) { }
}
public static Action Foo()
{
    while (true) { }
}

However, for these two methods:

public static void Foo()
{
    while (true) { break; }
}
public static Action Foo()
{
    while (true) { break; }
}

The first compiles, and the second does not. It has a code path that doesn't return a valid value.

In fact, while(true){} (along with throw new Exception();) is an interesting statement in that it is the valid body of a method with any return type.

Since the infinite loop is a suitable candidate for both overloads, and neither overload is "better", it results in an ambiguity error. The non-infinite loop implementation only has one suitable candidate in overload resolution, so it compiles.

Of course, to bring async back into play, it is actually relevant in one way here. For the async methods they both always return something, whether it's a Task or a Task<T>. The "betterness" algorithms for overload resolution will prefer delegates that return a value over void delegates when there is a lambda that could match either, however in your case the two overload both have delegates that return a value, the fact that for async methods returning a Task instead of a Task<T> is the conceptual equivalent of not returning a value isn't incorporated into that betterness algorithm. Because of this the non-async equivalent wouldn't result in an ambiguity error, even though both overloads are applicable.

Of course it's worth noting that writing a program to determine if an arbitrary block of code will ever complete is a famously unsolvable problem, however, while the compiler cannot correctly evaluate whether every single snippet will complete, it can prove, in certain simple cases such as this one, that the code will in fact never complete. Because of this there are ways of writing code that will clearly (to you and me) never complete, but that the compiler will treat as possibly completing.

这篇关于如何添加在循环决心超载歧义休息一下?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆