如何将Lambda表达式插入到Linq声明式查询表达式中?

5
假设您有以下代码:
  string encoded="9,8,5,4,9";

  // Parse the encoded string into a collection of numbers
  var nums=from string s in encoded.Split(',')
           select int.Parse(s);

那很简单,但如果我想将lambda表达式应用于select中的s,同时仍然将其保留为声明式查询表达式,该怎么办呢?换句话说:
  string encoded="9,8,5,4,9";

  // Parse the encoded string into a collection of numbers
  var nums=from string s in encoded.Split(',')
           select (s => {/* do something more complex with s and return an int */});

当然这段代码无法编译。但是,我如何在不转换为流畅语法的情况下插入一个lambda表达式呢?
更新:在StriplingWarrior的指导下,我有了一个复杂但可编译的解决方案。
var result=from string s in test.Split(',')
           select ((Func<int>) 
             (() => {string u="1"+s+"2"; return int.Parse(u);}))();

关键在于将其转换为 Func<string,int>,然后对每次选择迭代使用 (s) 的 lambda 进行评估。有没有更简单的方法(即,不需要将其转换为 Func 并进行评估,或者通过保持查询表达式语法来实现相同的最终结果的更少冗长的内容)?请注意:上面的 lambda 内容是微不足道且示例性质的,请勿更改它。更新2:是的,我是疯狂的 Mike,回来提供另一种(更漂亮?)的解决方案:
public static class Lambda
{
  public static U Wrap<U>(Func<U> f)
  {
    return f();
  }
}
... 
  // Then in some function, in some class, in a galaxy far far away:

  // Look what we can do with no casts
  var res=from string s in test.Split(',')
          select Lambda.Wrap(() => {string u="1"+s+"2"; return int.Parse(u);});

我认为这种方法可以解决问题,而且不需要使用丑陋的强制转换和冗长的括号。类似Lambda.Wrap的通用方法是否已经在.NET 4.0框架中出现,以便我不必重新发明轮子?为了不过多地负担这个讨论,我已经将这个问题移到了它自己的问题中:.NET 4.0中是否存在"Wrap"通用方法


1
我不明白为什么有人会想要使用声明式语法。它与 SQL 的相似之处似乎比帮助新手理解 LINQ 更加令人困惑。但无论如何,这是一个好问题。+1 - Klaus Byskov Pedersen
1
@klausbyskov,关于“为什么”的问题,社区中已经有很多回答了。对我而言,我并不太关心在这个问题中解决“为什么”,我只是想要一个“如何”的答案,如果可能的话。尽管我的例子不是最具表现力的语句,但我想使用一个简单的例子来不过度复杂化我的问题。 - Michael Goldshteyn
1
声明性语法在您有多个joinfrom时非常方便,否则将需要一些非常混乱的手工编码匿名类型。 - StriplingWarrior
为什么它无法编译?你收到了什么错误信息? - Gabe
错误信息为:错误 CS1942: 选择子句中表达式的类型不正确。在调用“Select”时推理类型失败。 - Michael Goldshteyn
显示剩余7条评论
5个回答

2
假设您正在使用LINQ to Objects,您可以使用一个帮助方法:
假設您正在使用LINQ to Objects,您可以使用一個幫助方法:
select DoSomethingComplex(s)

如果您不喜欢使用方法,可以使用Func:
Func<string, string> f = s => { Console.WriteLine(s); return s; };
var q = from string s in new[]{"1","2"}
        select f(s);

如果你非要把它放到行内,你可以这样做:
from string s in new[]{"1","2"}
select ((Func<string>)(() => { Console.WriteLine(s); return s; }))()

通过使用 {} 大括号,你实际上是在表示你的lambda表达式代表一个函数。 - StriplingWarrior
我认为你已经找到了答案。关键在于将lambda表达式的类型转换为Func<int>(而不是string)。让我再试一下,但你的答案已经走上了正确的轨道,所以现在给你一个+1... - Michael Goldshteyn
1
是的,在 Func<T> 中的 T 需要是你期望从表达式中获取的任何类型。他的示例使用了 string,因为最终他只是返回了 s - Anthony Pegram

0
你可以简单地这样做:
var nums = from string s in encoded.Split(',')
           select (s => { DoSomething(); return aValueBasedOnS; });

返回值告诉编译器结果集的类型。


我尝试过类似的东西,但它无法编译:var nums=from string s in test.Split('_') select {t => {int n=DoSomething(t); ... return n;}}; // 其中 DoSomething 被定义为:int DoSomething(string s) ... - Michael Goldshteyn
以上表达式的更正(仍无法编译):var result=from string s in test.Split('_') select (t => {return int.Parse(DoSomething(t));}); - Michael Goldshteyn

0
这样怎么样:
var nums= (from string s in encoded.Split(',') select s).Select( W => ...);

0
有人能想出更简单的方法吗?
是的。首先,你可以像这样重写它。
var result = from s in encoded.Split(',')
             select ((Func<int>)(() => int.Parse("1" + s + "2")))();

然而,那样的写法并不是很易读,尤其对于一个查询表达式来说。对于这个特定的查询和投影,可以使用let关键字。

var result = from s in encoded.Split(',')
             let t = "1" + s + "2"
             select int.Parse(t);

我不介意中间有 let,但是我怎么在那里放入一个 lambda 表达式,而不是 "1"+s+"2"。那只是一个例子... - Michael Goldshteyn
你可以像 Select 一样处理它。例如 let t = ((Func<string>)(() =>"1" + s + "2"))()。再次强调,这种写法可能会让人感到难以理解,尤其是如果你打算做更多的事情时。我再次提醒你的问题/评论让我对你的真实意图感到紧张。 - Anthony Pegram
@Michael,关键是当使用查询表达式语法时,你只需要通过将其转换为适用于表达式的任何Func<>来告诉编译器有关你的lambda。你还需要使用适当数量的参数调用表达式。但我想说一句话:要非常谨慎。 - Anthony Pegram
好的,经过转换和执行,基本上使得额外的t(和let)变得多余,我们回到了StriplingWarrior的原始解决方案。 - Michael Goldshteyn

0

使用编码.Split(',')方法返回一个IEnumerable整数序列,然后使用Select方法将每个字符串转换为整数。

编辑:

IEnumerable<int> integers = from s in encoded.Split(',') select int.Parse(string.Format("1{0}2",s));

我认为你没有理解问题的重点,那就是要避免调用.Select(...) - StriplingWarrior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接