当前位置:  开发笔记 > 编程语言 > 正文

C#3.0泛型类型推断 - 将委托作为函数参数传递

如何解决《C#3.0泛型类型推断-将委托作为函数参数传递》经验,为你挑选了4个好方法。

我想知道为什么当C#3.0编译器可以隐式地为同一方法创建委托时,它作为参数传递给泛型函数时无法推断方法的类型.

这是一个例子:

class Test
{
    static void foo(int x) { }
    static void bar(Action f) { }

    static void test()
    {
        Action f = foo; // I can do this
        bar(f); // and then do this
        bar(foo); // but this does not work
    }   
}

我本以为我能够传递foobar编译器推断出Action正在传递的函数的签名的类型,但这不起作用.但是我可以Actionfoo没有强制转换的情况下创建一个from ,所以有一个合理的原因,编译器也不能通过类型推断做同样的事情吗?



1> Amy B..:

也许这会让它更清晰:

public class SomeClass
{
    static void foo(int x) { }
    static void foo(string s) { }
    static void bar(Action f){}
    static void barz(Action f) { }
    static void test()
    {
        Action f = foo;
        bar(f);
        barz(foo);
        bar(foo);
        //these help the compiler to know which types to use
        bar(foo);
        bar( (int i) => foo(i));
    }
}

foo不是动作 - foo是一个方法组.

在赋值语句中,编译器可以清楚地告诉您正在讨论哪个foo,因为指定了int类型.

在barz(foo)语句中,编译器可以告诉您正在讨论哪个foo,因为指定了int类型.

在bar(foo)语句中,它可以是带有单个参数的任何foo - 因此编译器放弃了.

编辑:我添加了两种(更多)方法来帮助编译器找出类型(即 - 如何跳过推理步骤).

从我对JSkeet的回答中的文章的阅读中,不推断类型的决定似乎是基于相互推断的场景,例如

  static void foo(T x) { }
  static void bar(Action f) { }
  static void test()
  {
    bar(foo); //wut's T?
  }

由于一般问题是不可解决的,因此他们选择在解决方案存在的情况下留下未解决的特定问题.

作为此决定的结果,您不会为方法添加重载,并且会从用于单个成员方法组的所有调用方中获得大量类型混淆.我想这是件好事.



2> configurator..:

理由是,如果类型扩大,那么就不应该失败.即,如果方法foo(字符串)被添加到类型中,它应该对现有代码无关紧要 - 只要现有方法的内容不会改变.

因此,即使只有一个方法foo,也不能将对foo(称为方法组)的引用强制转换为非类型特定的委托,例如Action仅限于类型特定的委托,例如Action.


这是一个很好的观点 - 我没有想到这一点.但是,如果我之后添加了一个新的foo,那么编译器(恕我直言)应该打破并抱怨模糊,然后才会出现问题.现在,当没有其他foo时,它似乎应该工作.

3> Jon Skeet..:

这有点奇怪,是的.类型推断的C#3.0规范很难阅读并且有错误,但它看起来应该有效.在第一阶段(第7.4.2.1节)中,我认为存在错误 - 它不应该在第一个项目符号中提及方法组(因为它们没有被显式参数类型推断(7.4.2.7)覆盖 - 这意味着它应该使用输出类型推断(7.4.2.6).看起来它应该工作 - 但显然它不会:(

我知道MS正在寻求改进类型推断的规范,所以它可能会变得更清晰一些.我也知道,无论阅读的难度如何,对方法组和类型推断都有限制 - 当方法组实际上只是一种方法时,这种限制可能是特殊的.

Eric Lippert有一个关于返回类型推断的博客条目,不使用方法组,这这种情况类似 - 但是这里我们对返回类型不感兴趣,只对参数类型感兴趣.他的类型推断系列中的其他帖子可能会有所帮助.



4> Hans Passant..:

请记住作业

Action f = foo;

已经有很多语法糖.编译器实际上为此语句生成代码:

Action f = new Action(foo);

相应的方法调用编译没有问题:

bar(new Action(foo));

Fwiw,帮助编译器推断出类型参数:

bar(foo);

所以它归结为一个问题,为什么赋值语句中的糖而不是方法调用中的糖?我不得不猜测这是因为糖在分配中是明确的,只有一种可能的替代品.但是在方法调用的情况下,编译器编写者已经不得不处理重载解决问题.其规则非常详细.他们可能只是没有绕过它.

推荐阅读
jerry613
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有