如何解释该“呼叫是模棱两可的"?错误?

编程入门 行业动态 更新时间:2024-10-11 11:24:09
本文介绍了如何解释该“呼叫是模棱两可的"?错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

考虑这两种扩展方法,它们只是从任何类型的 T1 到 T2 的简单映射,以及一个重载以流畅地映射 Task< T> :

Consider these two extension methods which are just a simple map from any type T1 to T2, plus an overload to fluently map over Task<T>:

public static class Ext { public static T2 Map<T1, T2>(this T1 x, Func<T1, T2> f) => f(x); public static async Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f) => (await x).Map(f); }

现在,当我使用第二个重载并映射到引用类型时...

Now, when I use the second overload with a mapping to a reference type...

var a = Task .FromResult("foo") .Map(x => $"hello {x}"); // ERROR var b = Task .FromResult(1) .Map(x => x.ToString()); // ERROR

...我收到以下错误:

...I get the following error:

CS0121:以下方法或属性之间的调用不明确:"Ext.Map(T1,Func)"和"Ext.Map(Task,Func)"

CS0121: The call is ambiguous between the following methods or properties: 'Ext.Map(T1, Func)' and 'Ext.Map(Task, Func)'

映射到值类型可以很好地工作:

Mapping to a value type works fine:

var c = Task .FromResult(1) .Map(x => x + 1); // works var d = Task .FromResult("foo") .Map(x => x.Length); // works

但是只有在映射实际上使用输入来产生输出的情况下,

But only as long the mapping actually uses the input to produce an output:

var e = Task .FromResult(1) .Map(_ => 0); // ERROR

问题

有人可以向我解释一下这是怎么回事吗?我已经放弃寻找解决此错误的可行方法,但至少我想了解造成这种混乱的根本原因.

The Question

Can anyone please explain to me what is going on here? I've already given up on finding a feasible fix for this error, but at least I'd like to understand the root cause of this mess.

到目前为止,不幸的是,在我的用例中,我发现了三种不可接受的解决方法.首先是明确指定 Task< T1> .Map< T1,T2>()的类型参数:

So far I found three workarounds which are unfortunately not acceptable in my use case. The first is to specify the type arguments of Task<T1>.Map<T1,T2>() explicitly:

var f = Task .FromResult("foo") .Map<string, string>(x => $"hello {x}"); // works var g = Task .FromResult(1) .Map<int, int>(_ => 0); // works

另一种解决方法是不使用lambda:

Another workaround is to not use lambdas:

string foo(string x) => $"hello {x}"; var h = Task .FromResult("foo") .Map(foo); // works

第三个选择是将映射限制为内部功能(即 Func< T,T> ):

And the third option is to restrict the mappings to endofunctions (i.e. Func<T, T>):

public static class Ext2 { public static T Map2<T>(this T x, Func<T, T> f) => f(x); public static async Task<T> Map2<T>(this Task<T> x, Func<T, T> f) => (await x).Map2(f); }

我创建了.NET小提琴,您可以在其中亲自尝试上述所有示例.

I created a .NET Fiddle where you can try out all the above examples yourself.

推荐答案

根据C#规范,方法调用,接下来的规则用于将通用方法 F 视为方法调用的候选对象:

According to C# Specification, Method invocations, the next rules are used to consider a generic method F as a candidate for method invocation:

  • 方法具有与类型实参列表中提供的方法类型参数相同的数量,

    • Method has the same number of method type parameters as were supplied in the type argument list,

      一旦将类型参数替换为相应的方法类型参数,则参数列表中的所有构造类型 F 满足其约束条件(满足约束条件),并且 F 的参数列表适用于 A (适用函数成员). A -可选参数列表.

      Once the type arguments are substituted for the corresponding method type parameters, all constructed types in the parameter list of F satisfy their constraints (Satisfying constraints), and the parameter list of F is applicable with respect to A (Applicable function member). A - optional argument list.

      表达

      Task.FromResult("foo").Map(x => $"hello {x}");

      两种方法

      public static T2 Map<T1, T2>(this T1 x, Func<T1, T2> f); public static async Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f);

      满足以下要求:

      • 它们都有两个类型参数;
      • 其构建的变体

      • they both have two type parameters;
      • their constructed variants

      // T2 Map<T1, T2>(this T1 x, Func<T1, T2> f) string Ext.Map<Task<string>, string>(Task<string>, Func<Task<string>, string>); // Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f) Task<string> Ext.Map<string, string>(Task<string>, Func<string, string>);

    • 令人满意的类型约束(因为 Map 方法没有类型约束)并且可以根据可选参数应用(因为 Map 方法也没有可选参数).注意:定义使用类型推断的第二个参数(lambda表达式)的类型.

      satisfy type constraints (because there is no type constraints for Map methods) and applicable according to optional arguments (because also there is no optional arguments for Map methods). Note: to define the type of the second argument (lambda expression) a type inference is used.

      因此,在此步骤中,算法将两种变体都视为方法调用的候选者.对于这种情况,它使用重载分辨率来确定哪个候选者更适合调用.规范中的字词:

      So at this step the algorithm considers both variants as candidates for method invocation. For this case it uses Overload resolution to determine which candidate better fits for invocation. Words from specification:

      使用以下方法确定一组候选方法中的最佳方法重载解析的重载解析规则.如果一个最好方法无法识别,方法调用不明确,并且发生绑定时间错误.执行过载解析时,代入方法后,将考虑通用方法的参数为相应的方法键入参数(提供或推断)类型参数.

      The best method of the set of candidate methods is identified using the overload resolution rules of Overload resolution. If a single best method cannot be identified, the method invocation is ambiguous, and a binding time error occurs. When performing overload resolution, the parameters of a generic method are considered after substituting the type arguments (supplied or inferred) for the corresponding method type parameters.

      表达

      // I intentionally wrote it as static method invocation. Ext.Map(Task.FromResult("foo"), x => $"hello {x}");

      可以使用Map方法的构造变体用以下方式重写:

      can be rewritten the next way using constructed variants of the method Map:

      Ext.Map<Task<string>, string>(Task.FromResult("foo"), (Task<string> x) => $"hello {x}"); Ext.Map<string, string>(Task.FromResult("foo"), (string x) => $"hello {x}");

      重载解析使用更好的函数成员算法来定义这两种方法中的哪一种更适合方法调用.

      Overload resolution uses Better function member algorithm to define which of this two methods better fits method invocation.

      我已经多次阅读了此算法,还没有找到算法可以定义方法 Exp.Map< T1,T2>(任务< T1> ;, Func< T1,T2>)的地方

      I have read this algorithm several times and haven't found a place where the algorigthm can define the method Exp.Map<T1, T2>(Task<T1>, Func<T1, T2>) as better method for considered method invocation. In this case (when better method cannot be defined) a compile time error occures.

      总结:

      • 方法调用算法将这两种方法都视为候选方法;
      • 更好的函数成员算法无法定义更好的调用方法.

      另一种帮助编译器选择更好方法的方法(就像您在其他解决方法中所做的那样):

      Another approach of helping compiler to choose better method (as you did in your other workarounds):

      // Call to: T2 Map<T1, T2>(this T1 x, Func<T1, T2> f); var a = Task.FromResult("foo").Map( (string x) => $"hello {x}" ); // Call to: async Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f); var b = Task.FromResult(1).Map( (Task<int> x) => x.ToString() );

      现在,第一个类型参数 T1 已明确定义,并且不会出现歧义.

      Now the first type argument T1 is explicitly defined and an ambiguity does not occur.

更多推荐

如何解释该“呼叫是模棱两可的"?错误?

本文发布于:2023-10-18 04:21:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1503088.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:模棱两可   错误   quot

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!