对于没有使用Lambda Expresstions经验的人,以下代码使其看起来像魔术:
int totalScore = File.ReadLines(@"c:/names.txt")
.OrderBy(name => name)
.Select((name, index) => {
int score = name.AsEnumerable().Select(character => character - 96).Sum();
score *= index + 1;
return score;
})
.Sum();
是什么让 name 引用集合中的元素,更有趣的是,是什么让 index 引用元素的索引?
因为除了理解Delegates(也许还有别的什么?)之外,这显然不是一种魔力,Lambda表达式如何运作?
答案 0 :(得分:4)
没有魔法,所有Select
正在执行
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector) {
if (source == null) throw Error.ArgumentNull("source");
if (selector == null) throw Error.ArgumentNull("selector");
return SelectIterator<TSource, TResult>(source, selector);
}
static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector) {
int index = -1;
foreach (TSource element in source) {
checked { index++; }
yield return selector(element, index);
}
}
selector
是你传入的函数,它是Func<TSource, int, TResult>
,这意味着它需要两个参数,第一个参数可以是任何类型,第二个参数是int
和返回类型可以是任何类型。
您正在使用的功能是匿名功能
(name, index) => {
int score = name.AsEnumerable().Select(character => character - 96).Sum();
score *= index + 1;
return score;
}
与
相同private int SomeFunction(string name, int index)
{
int score = name.AsEnumerable().Select(character => character - 96).Sum();
score *= index + 1;
return score;
}
因此Select
传递name
和index
值并调用您的函数。
答案 1 :(得分:2)
这实际上与lambdas无关,你可以轻松地传递一个带有字符串和int
参数的方法。您可以编写自己的选择实现:
public static class TestClass
{
public static IEnumerable<TReturnType> DoSelect<TSourceType, TReturnType>(this IEnumerable<TSourceType> source, Func<TSourceType, int, TReturnType> action)
{
int i = 0;
foreach(var row in source)
yield return action(row, i++);
}
}
并像这样使用它:
var myStr = new List<String> { "A", "B" }.DoSelect((name, index) => {return name+index; });
myStr.Dump();
结果:A0
,B1
Select只是一个对集合进行操作的扩展方法。它的实现决定了索引,以及使用它需要什么参数。
如果您对yield
的工作方式不太确定,此方法会产生类似的结果(但不会延迟执行):
public static IEnumerable<TReturnType> DoSelect<TSourceType, TReturnType>(this IEnumerable<TSourceType> source, Func<TSourceType, int, TReturnType> action)
{
int i = 0;
var returnList = new List<TReturnType>();
foreach(var row in source)
returnList.Add(action(row, i++));
return returnList;
}
您也可以根据选择编写没有lambdas的代码:
private int MyFunction(string name, int index)
{
int score = name.AsEnumerable().Select(character => character - 96).Sum();
score *= index + 1;
return score;
}
int totalScore = File.ReadLines(@"c:/names.txt")
.OrderBy(name => name)
.Select(MyFunction)
.Sum();