【问题标题】:Replacing a regular method with an anonymous method in C#/LINQ在 C#/LINQ 中用匿名方法替换常规方法
【发布时间】:2011-01-02 15:40:20
【问题描述】:

我有一个如下所示的 LINQ 查询:

public IEnumerable<Foo> SelectFooBars()
{
    return
        from
            f in foos
        join
            b in bars
            on f.BarId equals b.Id
        select
            AddMissingProp(f, b.MissingProp);
}

public void AddMissingProp(Foo foo, string missingProp) // substitute this with inline lambda
{
    foo.MissingProp = missingProp;
    return foo;
}

我想摆脱 AddMissingProp 并在我的 select 子句中使用某种形式的 lambda。

我尝试过……

...
select
    (f, b) => { f.MissingProp = b.MissingProp; return f }

...但我收到以下错误:

不能在此范围内声明名为“f”的局部变量,因为它会给“f”赋予不同的含义,后者已在“父级或当前”范围内用于表示其他内容。

如何“lambda-ize”我的查询?


更新

这也行不通:

...
select
    () => { f.MissingProp = b.MissingProp; return f }

我收到以下错误:

连接子句中的一个表达式的类型不正确。调用“加入”时类型推断失败。

我根本没有更改join 子句,所以我很困惑。

【问题讨论】:

  • 我不太了解 LINQ 语法,无法将其写出来,但在 foreach(yourLinqQuery) 语句中进行变异不是更好吗?对对象进行选择变异似乎令人困惑。
  • @Doc Brown,这实际上只是我的示例中的一个错字(我的真实代码是equals)。现已修复。

标签: c# linq lambda anonymous-methods


【解决方案1】:

我认为 icambron 是对的,恕我直言,更好的可读版本是这样的:

  var fooBar = from 
                 f in foos
               join 
                 b in bars
                 on f.BarId equals b.Id 
               select new {f,b};

   foreach(var x in fooBar)
        x.f.MissingProp = x.b.MissingProp;

   // EDIT due to comments: add this if you 
   // need IEnumerable<Foo> returned
   return fooBar.Select(fb => fb.f);

from join select 语句用于查询,不应滥用它们来改变序列的内容。

编辑:这是另一个link,提供了一些见解,为什么使用ForEach 的函数形式不是一个好主意。

【讨论】:

  • 谢谢+1。我曾考虑过使用foreach,但我想我想太花哨了:) 这是一个很好的解决方案。
  • 糟糕,一个问题。这给我留下了一个IEnumerable&lt;anonymous_type_foo_bar_mashup&gt;,但我需要一个IEnumerable&lt;foo&gt;。我认为如果您在答案中添加return fooBar.Select(fb =&gt; fb.f);,那将是正确的。
  • 你应该也能做到var foobar = (....).ForEach(....); 和一行吧!
  • @DanThMan:编辑了它,但是您应该更改 AddMissingProp 的返回类型以使原始问题适合编辑后的答案 :-)
  • @Chris:ForEach 适用于 Array,而不适用于 IEnumerable。而且我认为这不会使其更具可读性。
【解决方案2】:

您可以在 lambda 表达式中为参数指定类型,但您需要使用不同的名称,因为您已经在查询中使用了 f 和 b。

(Foo f1, Bar b1) => ...

编辑

return
(
    from 
        f in foos 
    join
        b in bars 
        on f.BarId equals b.Id 
    select 
        new {f, b}
).select(foobar => {foobar.f.BarId = foobar.b.Id; return foobar.f});

【讨论】:

  • 这不起作用。它不知道应该传入fb。我也收到与使用 () =&gt; { ... } 相同的错误(请参阅我的更新)。
  • 第二次魅力:) 好吧,除了第二次from 应该是join。但是,除此之外,这是我迄今为止最喜欢的解决方案。
  • 我注意到另一个小问题...应该是{foobar.f.BarId = foobar.b.Id; return foobar.f};。或者,您可以在 new {f, b} 中显式定义匿名类型的属性名称,如下所示:new { foo = f, bar = b}
  • 啊,是的……那当然是对的 :-) 当我真正关心的是结构时,我通常不关心属性名称。它也更符合函数式编程,您通常在处理结构类型系统而不是名义上的系统。顺便说一句,我认为我个人非常喜欢函数式编程的原因之一是它可以让您避免计算机科学中两个最困难的问题之一(缓存失效和命名事物:-))。干杯...
  • 我编辑了您的答案以删除我们讨论的错误,因为我想将其标记为答案。
【解决方案3】:

选择 (f2, b2) => { f2.MissingProp = b2.MissingProp;返回 f2 }

【讨论】:

  • 这不起作用。它不知道应该传入fb。我也得到与使用 () => { ... } 相同的错误(请参阅我的更新)。
【解决方案4】:

用 Lambda 语法重写。

var vf2 = foos.Join(bars, f => f.id, b => b.id, (foo, bar) => { foo.MissingProp = bar.MissingProp; return foo; });

如果您需要解释此语法,请告诉我。

【讨论】:

  • 由于某种原因,这会导致 join 子句出错(即使我没有更改 join 子句): join 子句中的其中一个表达式的类型不正确。调用“加入”时类型推断失败。
  • 那是因为当你选择你告诉它要返回哪个类型时, select () 假定你正在返回一个委托类型。我认为你应该坚持你的方法。
  • 嗯...也许就是做不到。但是为什么它不能从return f; 推断出返回类型呢?它知道 f 是Foo
  • @Dan,那是因为 select () 意味着选择新的委托,而不是选择新的 Foo。
  • 哇,它确实适用于扩展方法。我真的很讨厌Join() 扩展方法的可读性,但它确实有效。感谢您的帮助。
【解决方案5】:

如果编译器无法推断出要传递给 lambda 的正确类型,您当然可以自己指定类型。

这应该可以正常工作:

select
    (Foo f2, b) => { f2.MissingProp = b.MissingProp; return f2; }

请注意,正如您已经注意到的那样,您不能重复使用 f 并希望它保留其含义。这是一个新的方法签名,带有新的参数,因此您需要为它使用一个不同的名称。

当您这样做时,您会注意到编译器无法自行确定第一个参数应该是什么类型,但您可以像上面一样指定它。

【讨论】:

  • 至于不完整的代码,我忘记了我的return语句。刚刚修好了。
  • 您还错过了 Linq 查询后的结束分号。您应该始终努力复制和粘贴代码中的内容,而不是试图简化它。你会惊讶于这里的人能够阅读代码:)
  • 无法让您的解决方案发挥作用。即使我使用 (Foo f2, Bar b) 而不是 (Foo f2, b),我仍然会因为我的 join 子句而被标记。
  • 我同意复制/粘贴...在这种情况下,我的代码中有专有信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
相关资源
最近更新 更多