C#lambda幕后

时间:2015-08-12 04:04:55

标签: c# lambda

对于没有使用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表达式如何运作?

2 个答案:

答案 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传递nameindex值并调用您的函数。

答案 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();

结果:A0B1

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();
相关问题