Created
June 12, 2011 22:39
-
-
Save takeshik/1022066 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class DispatchHelper | |
{ | |
public static Tuple<MethodInfo, Expression[]> DispatchMethod(IEnumerable<MethodInfo> methods, IList<Type> typeArguments, IList<Expression> arguments) | |
{ | |
return methods | |
.Select(m => Tuple.Create(m, new Dictionary<Type, Type>(), arguments)) | |
.If(_ => typeArguments != null && typeArguments.Any(), s => s | |
.Where(_ => _.Item1.IsGenericMethodDefinition && _.Item1.GetGenericArguments().Length == typeArguments.Count) | |
.Select(_ => _.Item1.MakeGenericMethod(typeArguments.ToArray()) | |
.Let(m => Tuple.Create(m, m.GetGenericParameterMap(), arguments)) | |
) | |
) | |
.Where(t => t.Item1.GetParameters().If(_ => t.Item1.IsParamArrayMethod(), | |
ps => t.Item3.Count >= ps.Length - 1 | |
&& ps.SkipLast(1).Zip(t.Item3, | |
(p, a) => IsAppropriate(p.ParameterType, a.Type) | |
).All(_ => _) | |
&& EnumerableEx.Repeat(ps.Last()).Zip(t.Item3.Skip(ps.Length - 1), | |
(p, a) => IsAppropriate(p.ParameterType.GetElementType(), a.Type) | |
).All(_ => _), | |
ps => arguments.Count == ps.Length | |
&& ps.Zip(arguments, | |
(p, a) => IsAppropriate(p.ParameterType, a.Type) | |
).All(_ => _) | |
)) | |
.Select(t => t | |
.If(_ => _.Item1.IsGenericMethod, _ => | |
InferTypeArguments(t.Item1.GetParameters().Select(p => p.ParameterType), t.Item3.Select(e => e.Type)) | |
.Let(m => Tuple.Create(_.Item1.MakeGenericMethod(m.Values.ToArray()), m, _.Item3)) | |
) | |
.If(_ => _.Item1.IsParamArrayMethod(), _ => | |
_.Item1.GetParameters().Let(ps => | |
ps.Last().ParameterType.GetElementType().Let(et => | |
Tuple.Create(_.Item1, _.Item2, (IList<Expression>) _.Item3 | |
.Take(ps.Length - 1) | |
.Concat(EnumerableEx.Return(Expression.NewArrayInit(et, _.Item3 | |
.Skip(ps.Length - 1) | |
.Select(e => e.Type == et ? e : Expression.Convert(e, et)) | |
))) | |
.ToArray() | |
) | |
) | |
) | |
) | |
) | |
.OrderBy(t => t.Item1.IsParamArrayMethod()) | |
.ThenByDescending(t => t.Item2.Count) | |
.First() | |
.Let(t => Tuple.Create( | |
t.Item1, | |
t.Item1.GetParameters() | |
.Select(p => p.ParameterType) | |
.Zip(t.Item3, (pt, a) => pt != a.Type ? Expression.Convert(a, pt) : a) | |
.ToArray() | |
)); | |
} | |
public static Dictionary<Type, Type> InferTypeArguments(IEnumerable<Type> parameters, IEnumerable<Type> arguments) | |
{ | |
return parameters | |
.Where(t => t.IsGenericParameter || t.IsGenericType) | |
.Zip(arguments, (p, a) => p.IsGenericParameter | |
? EnumerableEx.Return(Tuple.Create(p, a)) | |
: p.GetGenericArguments() | |
.Zip(a.GetConvertibleTypes() | |
.Single(t => t.IsGenericType && p.GetGenericTypeDefinition() == t.GetGenericTypeDefinition()) | |
.GetGenericArguments(), | |
Tuple.Create | |
) | |
) | |
.SelectMany(_ => _) | |
.Distinct() | |
.ToDictionary(_ => _.Item1, _ => _.Item2); | |
} | |
public static IEnumerable<Type> GetConvertibleTypes(this Type type) | |
{ | |
return EnumerableEx.Concat( | |
EnumerableEx.Generate(type, t => t != null, t => t.BaseType, _ => _), | |
type.GetInterfaces() | |
); | |
} | |
public static Boolean IsParamArrayMethod(this MethodBase method) | |
{ | |
return method.GetParameters() | |
.Let(_ => _.Any() && Attribute.IsDefined(method.GetParameters().Last(), typeof(ParamArrayAttribute))); | |
} | |
public static Dictionary<Type, Type> GetGenericParameterMap(this Type type) | |
{ | |
return type.GetGenericTypeDefinition().GetGenericArguments() | |
.Zip(type.GetGenericArguments(), Tuple.Create) | |
.ToDictionary(_ => _.Item1, _ => _.Item2); | |
} | |
public static Dictionary<Type, Type> GetGenericParameterMap(this MethodInfo method) | |
{ | |
return method.GetGenericMethodDefinition().GetGenericArguments() | |
.Zip(method.GetGenericArguments(), Tuple.Create) | |
.ToDictionary(_ => _.Item1, _ => _.Item2); | |
} | |
public static Boolean IsAppropriate(Type parameter, Type argument) | |
{ | |
return parameter.IsGenericParameter | |
? argument.GetConvertibleTypes() | |
.Let(ts => parameter.GetGenericParameterConstraints().All(c => ts.Contains(c))) | |
: argument.GetConvertibleTypes().If( | |
_ => parameter.ContainsGenericParameters, | |
_ => _.Select(t => t.IsGenericType ? t.GetGenericTypeDefinition() : t) | |
).Let(_ => _.Contains(parameter.IsGenericType ? parameter.GetGenericTypeDefinition() : parameter)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment