Lambda for Dummies....anyone, anyone? I think not

c#-3.0, lambda

Solution

Let's dissect your code sample:

filenames.SelectMany(f => 
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
        .Cast<PluginClassAttribute>()
        .Select(a => a.PluginType)
).ToList();

So, we start off with a `string[]` called `filenames`. We invoke the `SelectMany` extension method on the array, and then we invoke `ToList` on the result:

filenames.SelectMany(
   ...
).ToList();

`SelectMany` takes a delegate as parameter, in this case the delegate must take one parameter of the type `string` as input, and return an `IEnumerable<T>` (Where the type of `T` is inferred). This is where lambdas enter the stage:

filenames.SelectMany(f => 
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
).ToList()

What will happen here is that for each element in the `filenames` array, the delegate will be invoked. `f` is the input parameter, and whatever comes to the right of `=>` is the method body that the delegate refers to. In this case, `Assembly.LoadFrom` will be invoked for filename in the array, passing he filename into the `LoadFrom` method using the `f` argument. On the `AssemblyInstance` that is returned, `GetCustomAttributes(typeof(PluginClassAttribute), true)` will be invoked, which returns an array of `Attribute` instances. So the compiler can not infer that the type of `T` mentioned earlier is `Assembly`.

On the `IEnumerable<Attribute>` that is returned, `Cast<PluginClassAttribute>()` will be invoked, returning an `IEnumerable<PluginClassAttribute>`.

So now we have an `IEnumerable<PluginClassAttribute>`, and we invoke `Select` on it. The `Select` method is similar to `SelectMany`, but returns a single instance of type `T` (which is inferred by the compiler) instead of an `IEnumerable<T>`. The setup is identical; for each element in the `IEnumerable<PluginClassAttribute>` it will invoke the defined delegate, passing the current element value into it:

.Select(a => a.PluginType)

Again, `a` is the input parameter, `a.PluginType` is the method body. So, for each `PluginClassAttribute` instance in the list, it will return the value of the `PluginType` property (I will assume this property is of the type `Type`).

Executive Summary If we glue those bits and pieces together:

// process all strings in the filenames array
filenames.SelectMany(f => 
        // get all Attributes of the type PluginClassAttribute from the assembly
        // with the given file name
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
        // cast the returned instances to PluginClassAttribute
        .Cast<PluginClassAttribute>()
        // return the PluginType property from each PluginClassAttribute instance
        .Select(a => a.PluginType)
).ToList();

Lambdas vs. Delegates Let's finish this off by comparing lambdas to delegates. Take the following list:

List<string> strings = new List<string> { "one", "two", "three" };

Say we want to filter out those that starts with the letter "t":

var result = strings.Where(s => s.StartsWith("t"));

This is the most common approach; set it up using a lambda expression. But there are alternatives:

Func<string,bool> func = delegate(string s) { return s.StartsWith("t");};
result = strings.Where(func);

This is essentially the same thing: first we create a delegate of the type `Func<string, bool>` (that means that it takes a `string` as input parameter, and returns a `bool`). Then we pass that delegate as parameter to the `Where` method. This is what the compiler did for us behind the scenes in the first sample (`strings.Where(s => s.StartsWith("t"));`).

One third option is to simply pass a delegate to a non-anonymous method:

private bool StringsStartingWithT(string s)
{
    return s.StartsWith("t");
}

// somewhere else in the code:
result = strings.Where(StringsStartingWithT);

So, in the case that we are looking at here, the lambda expression is a rather compact way of defining a delegate, typically referring an anonymous method.

And if you had the energy read all the way here, well, thanks for your time :)

Problem

In my quest to understand the very odd looking ' => ' operator, I have found a good place to start, and the author is very concise and clear: ``` parameters => expression ``` Does anyone have any tips on understanding the basics of lambdas so that it becomes easier to 'decipher' the more complex lambda statements? For instance: if I am given something like (from an answer I received here): ``` filenames.SelectMany(f => Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) .Cast<PluginClassAttribute>() .Select(a => a.PluginType) ).ToList(); ``` How can I go about breaking this down into more simple pieces? UPDATE: wanted to show off my first lambda expression. Don't laugh at me, but I did it without copying someone's example...and it worked the first time: ``` public ModuleData[] GetStartModules( ) { return modules.FindAll(start => start.IsBatch == true).ToArray(); } ```

Original source