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(); } ```