Analicemos el ejemplo de código:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
.Cast<PluginClassAttribute>()
.Select(a => a.PluginType)
).ToList();
Por lo tanto, partimos de una llamada string[]
filenames
. Invocamos el método SelectMany
extensión en la matriz, y luego invocamos ToList
en el resultado:
filenames.SelectMany(
...
).ToList();
SelectMany
toma un delegado como parámetro, en este caso el delegado debe tener un parámetro del tipo string
como entrada, y devuelva un IEnumerable<T>
(donde se infiere el tipo de T
). Aquí es donde entran en la etapa lambdas:
filenames.SelectMany(f =>
Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
).ToList()
¿Qué ocurrirá aquí es que para cada elemento de la matrizfilenames
, el delegado se invocará. f
es el parámetro de entrada, y lo que viene a la derecha de =>
es el cuerpo del método al que hace referencia el delegado. En este caso, Assembly.LoadFrom
se invocará para el nombre de archivo en la matriz, pasando el nombre de archivo en el método LoadFrom
utilizando el argumento f
. En el AssemblyInstance
que se devuelve, se invocará GetCustomAttributes(typeof(PluginClassAttribute), true)
, que devuelve una matriz de instancias Attribute
. Por lo tanto, el compilador no puede inferir que el tipo de T
mencionado anteriormente es Assembly
.
En el IEnumerable<Attribute>
que se devuelve, se invocará Cast<PluginClassAttribute>()
, devolviendo un IEnumerable<PluginClassAttribute>
.
Así que ahora tenemos un IEnumerable<PluginClassAttribute>
, e invocamos Select
en él.El método Select
es similar a SelectMany
, pero devuelve una única instancia del tipo T
(que se deduce del compilador) en lugar de IEnumerable<T>
. La configuración es idéntica; para cada elemento en el IEnumerable<PluginClassAttribute>
que invocará el delegado definido, pasando el valor del elemento corriente en él:
.Select(a => a.PluginType)
Una vez más, a
es el parámetro de entrada, a.PluginType
es el cuerpo del método. Por lo tanto, para cada instancia PluginClassAttribute
en la lista, devolverá el valor de la propiedad PluginType
(supongo que esta propiedad es del tipo Type
).
Resumen Ejecutivo
Si pegamos esos trozos y piezas juntas:
// 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 delegados
Vamos a terminar esto mediante la comparación de lambdas a los delegados. Tome la siguiente lista:
List<string> strings = new List<string> { "one", "two", "three" };
decir que queremos filtrar los que comienza con la letra "t":
var result = strings.Where(s => s.StartsWith("t"));
Este es el método más común; configúralo usando una expresión lambda. Pero hay alternativas:
Func<string,bool> func = delegate(string s) { return s.StartsWith("t");};
result = strings.Where(func);
Esto es esencialmente lo mismo: primero creamos un delegado del tipo Func<string, bool>
(lo que significa que se necesita un string
como parámetro de entrada y devuelve un bool
). Luego pasamos ese delegado como parámetro al método Where
. Esto es lo que el compilador hizo para nosotros detrás de escena en la primera muestra (strings.Where(s => s.StartsWith("t"));
).
Una tercera opción es simplemente pasar un delegado a un método no anónimo:
private bool StringsStartingWithT(string s)
{
return s.StartsWith("t");
}
// somewhere else in the code:
result = strings.Where(StringsStartingWithT);
Así, en el caso que estamos viendo aquí, la expresión lambda es una forma más compacta de la definición de una delegar, generalmente refiriendo un método anónimo.
Y si tuviera la energía leído hasta aquí, bueno, gracias por su tiempo :)
Básicamente, lambdas son como cualquier otra función, excepto que se definen donde los necesite. ¿Estás familiarizado con las clases anónimas? son así solo por métodos. (publicado como un comentario porque no podía esperar acercarme a la calidad de las otras respuestas) – RCIX
Voy a insertar descaradamente una referencia de Jon Skeet: http://csharpindepth.com/Articles/Chapter5/Closures.aspx Esto realmente tiene ayudó a aumentar mi comprensión de las expresiones lambda. Muy buena lectura, por supuesto! – IAbstract
Te recomiendo que visites Lambdaexpression.net – Delashmate