【问题标题】:C# why is this type inferred method call ambiguous? [duplicate]C#为什么这种类型推断的方法调用不明确? [复制]
【发布时间】:2015-07-28 02:57:21
【问题描述】:

以下函数对尝试复制 C# 6.0 中可用的空条件运算符:

public static TResult Bind<T, TResult>(this T obj, Func<T, TResult> func)
    where T : class
{
    return obj == null ? default(TResult) : func(obj);
}

public static TResult Bind<T, TResult>(this Nullable<T> obj, Func<T, TResult> func)
    where T : struct
{
    return obj.HasValue ? func(obj.Value) : default(TResult);
}

第一个函数仅限于类,对于 String s,我可以编写如下内容:

var x = s.Bind(a => a.Substring(1));

第二个功能是我遇到麻烦的地方。例如,给定一个int? number 我想写:

var y = number.Bind(a => a + 1);

但是,这给了我以下错误:

以下方法或属性之间的调用不明确:'BindingExtensions.Bind(T, Func)' 和 'BindingExtensions.Bind(T?, Func)'

我猜这与匿名函数的类型推断和方法重载解析之间的相互作用有关。如果我将 a 的类型指定为 int ,则它编译得很好。

var y = number.Bind((int a) => a + 1);

但是,这显然不太理想。谁能告诉我为什么编译器认为上述绑定调用是模棱两可的和/或提供解决此问题的方法?我知道我可以简单地将这两个函数命名为不同的名称,但这有什么好玩的呢?

【问题讨论】:

  • 您可以删除第一个约束,而不再需要第二个约束。当然,对于 null 值,number.Bind(a=&gt;a+1) 的结果将是 null 而不是 0。但这就是我所期望的结果。
  • @juharr 但这会破坏目的。我希望匿名函数的输入永远不会为空,这就是为什么第二个函数在 Nullable 上运行但只将 T 传递给匿名函数的原因。
  • 你的函数不会被传递一个null,因为obj == null对于Nullable&lt;T&gt;来说是真的,而HasValue是假的。当然,这意味着您的函数必须期待 Nullable&lt;int&gt; 而不是 int,但这就是我所期望的。
  • @juharr 我不同意,我希望int 而不是int? 正是因为它永远不能为空。我对此的灵感来自于 monads 的功能思想(这就是我将函数命名为 Bind 的原因)。我认为Bind 可以将变量从其可为空的上下文中取出并对其进行操作。

标签: c#


【解决方案1】:

重载函数不能通过类型约束来消除歧义(参见“Generic constraints, where T : struct and where T : class”)。任何可空类型 N 都满足 N : TN : Nullable&lt;T&gt;,前者和后者分别要求 Bind 定义。我猜numberNullable&lt;int&gt; 或类似类型。

var x = s.Bind(a => a.Substring(1));

这是明确的,因为sstring 类型,并且对于所有T 而不是string : Nullable&lt;T&gt;,所以只有第一个重载是可以接受的。

var y = number.Bind(a => a + 1);

这是不明确的,因为a =&gt; a + 1 的类型可能被推断为Func&lt;int?,int?&gt;Func&lt;int,int&gt;。如果推断为Func&lt;int?,int?&gt;,则应用第一个重载,如果推断为Func&lt;int,int&gt;,则应用第二个重载。

var y = number.Bind((int a) => a + 1);

例如,如果 number 的类型为 Nullable&lt;int&gt;,则这是明确的。对于所有T 而不是T : Nullable&lt;int&gt;T : int 的第一次重载,因此它不适用。对于第二个重载,您只需要T : intT = int 很容易满足。

【讨论】:

  • Nullable&lt;T&gt; 本身就是一个结构,不是吗? link 那么int? 怎么能满足类约束呢?
  • @Micah 感谢您指出这个错误。我已经修改了我对问题的解释。
【解决方案2】:

试试这个:

    public static TResult Bind<T, TResult>(this T? obj, Func<T?, TResult> func)
    where T : struct
    {
        return obj.HasValue ? func(obj.Value) : default(TResult);
    }

【讨论】:

  • 这解决了问题,你需要解释我为什么认为
  • 这确实有效,但我真的很想看看解释。以为我不认为 OP 会对将 Nullable 传递给函数感到满意。
  • 对,这会起作用,但请参阅我在这个问题上所做的 cmets。 func 的参数应该是 T 而不是 T?,因为它永远不会为空。
  • @MicahHahn 如果参数永远不会为空,那么为什么参数是TT? 很重要
  • Func(T) 委托需要“相同”的 obj 类型 T。是的,它可以是 null 值,如果“int?number = null”,您仍然可以使用 Bind 函数,因为 obj.HasValue 将返回 false。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多