【问题标题】:dynamic, linq and Select()动态、linq 和 Select()
【发布时间】:2011-08-18 07:18:18
【问题描述】:

考虑以下(毫无意义,但仅用于说明目的)测试类:

public class Test
{
    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => ToStr(x));
    }

    public IEnumerable<string> ToEnumerableStrsWillCompile(IEnumerable<dynamic> t)
    {
        var res = new List<string>();

        foreach (var d in t)
        {
            res.Add(ToStr(d));
        }

        return res;
    }

    public string ToStr(dynamic d)
    {
        return new string(d.GetType());
    }
}

为什么在t.Select(x =&gt; ToStr(x)) 上编译时出现以下错误?

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<dynamic>' 
to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion 
exists (are you missing a cast?)

第二种方法没有错误。

【问题讨论】:

    标签: c# linq dynamic c#-4.0


    【解决方案1】:

    我相信这里发生的情况是,由于表达式ToStr(x) 涉及一个dynamic 变量,所以整个表达式的结果类型也是dynamic;这就是为什么编译器认为它有一个IEnumerable&lt;dynamic&gt;,它期望一个IEnumerable&lt;string&gt;

    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => ToStr(x));
    }
    

    有两种方法可以解决此问题。

    使用显式转换:

    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => (string)ToStr(x));
    }
    

    这告诉编译器表达式的结果肯定是一个字符串,所以我们最终得到一个IEnumerable&lt;string&gt;

    将 lambda 替换为方法组:

    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(ToStr);
    }
    

    这样,编译器将方法组表达式隐式转换为 lambda。请注意,由于表达式中没有提及dynamic变量x,因此可以立即推断其结果的类型为string,因为只需考虑一种方法,其返回类型为@987654333 @。

    【讨论】:

    • +1 用于解释,尤其是 Method Group 版本
    【解决方案2】:

    C# 编译器似乎将第一个方法 x =&gt; ToStr(x) 中的 lambda 类型确定为 Func&lt;dynamic, dynamic&gt;,因此将返回的 IEnumerable 的类型声明为 IEnumerable&lt;dynamic&gt;x =&gt; (string)ToStr(x) 的一个小改动似乎可以解决它。

    这很可能是因为类型推断规则 - 因为如果您将行更改为:

    return t.Select<dynamic, string>(x => ToStr(x));
    

    它编译没有错误。

    但是,我不太确定有问题的特定类型推断规则 - 但是,如果您采用以下代码:

    public void foo(dynamic d)
    {
      var f = this.ToStr(d);
      string s = f;
    }
    

    然后将鼠标悬停在编辑器中的“f”上,您将看到智能感知将表达式的类型报告为“动态 f”。这是因为this.ToStr(d) 是一个动态表达式,无论方法本身及其返回类型在编译时是否已知。

    然后编译器很乐意分配string s = f;,因为它能够静态分析f 可能是的类型,因为最终ToStr 总是返回一个字符串。

    这就是为什么第一种方法需要强制类型转换或显式类型参数的原因——因为编译器将ToStr 设为dynamic 类型;因为它至少有一个动态表达式作为它的一部分。

    【讨论】:

      【解决方案3】:

      试试这样:

      public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
      {
          return t.Select(ToStr);
      }
      

      另一种可能性是明确指定泛型参数:

      public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
      {
          return t.Select<dynamic, string>(x => ToStr(x));
      }
      

      【讨论】:

        【解决方案4】:

        试试return t.Select(x =&gt; ToStr(x)) as IEnumerable&lt;string&gt;

        【讨论】:

          猜你喜欢
          • 2015-11-10
          • 2014-06-02
          • 1970-01-01
          • 1970-01-01
          • 2012-09-03
          • 1970-01-01
          • 2019-06-19
          • 2012-08-16
          • 1970-01-01
          相关资源
          最近更新 更多