Just as many people believe tenaciously in small functions, some people believe lambdas should only contain small code fragments.
An often overlooked advantage of lambdas however, is using them you can encapsulate behavior which you would otherwise have to make available to the entire class.
Isn't it is a true advantage to maintain proper encapsulation, regardless of line count? Which possible disadvantages of using anonymous functions with a lot of lines of code am I overlooking?
The following is an actual example. argumentsMatch
does very specific argument matching, highly dependent on the behavior of the function in which it is defined. The following code IMHO follows the Single Responsibility Principle. Moving argumentsMatch
to a private method would result in it only being called from within this method.
/// <summary>
/// Get the first found matching generic type.
/// The type parameters of the generic type are optional.
/// E.g. Dictionary<,>
/// When full (generic) type is known (e.g. Dictionary<string,string>),
/// the "is" operator is most likely more performant,
/// but this function will still work correctly.
/// </summary>
/// <param name = "source">The source for this extension method.</param>
/// <param name = "type">The type to check for.</param>
/// <returns>
/// The first found matching complete generic type,
/// or null when no matching type found.
/// </returns>
public static Type GetMatchingGenericType( this Type source, Type type )
{
Type[] genericArguments = type.GetGenericArguments();
Type rawType = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
// Used to compare type arguments and see whether they match.
Func<Type[], bool> argumentsMatch
= arguments => genericArguments
.Zip( arguments, Tuple.Create )
.All( t => t.Item1.IsGenericParameter || // No type specified.
t.Item1 == t.Item2 );
Type matchingType = null;
if ( type.IsInterface )
{
// Traverse across all interfaces to find a matching interface.
matchingType =
(from t in source.GetInterfaces()
let rawInterface = t.IsGenericType ? t.GetGenericTypeDefinition() : t
where rawInterface == rawType &&
argumentsMatch( t.GetGenericArguments() )
select t).FirstOrDefault();
}
else
{
// Traverse across the type, and all it's base types.
Type baseType = source;
while ( baseType != null && baseType != typeof( object ) )
{
Type rawCurrent = baseType.IsGenericType
? baseType.GetGenericTypeDefinition()
: baseType;
if ( rawType == rawCurrent )
{
// Same raw generic type, compare type arguments.
if ( argumentsMatch( baseType.GetGenericArguments() ) )
{
matchingType = baseType;
break;
}
}
baseType = baseType.BaseType;
}
}
return matchingType;
}