Skip to content

Instantly share code, notes, and snippets.

@imgen
Last active March 29, 2019 06:23
Show Gist options
  • Save imgen/902047a615ddc1c48a89bbe78b0f01f6 to your computer and use it in GitHub Desktop.
Save imgen/902047a615ddc1c48a89bbe78b0f01f6 to your computer and use it in GitHub Desktop.
using System;
using System.Linq.Expressions;
using System.Reflection;
public static class ReflectionUtils
{
public static MethodInfo GetMethod<TInput, TOutput>(Expression<Func<TInput, TOutput>> methodCallExpression) => GetMethodByLambdaExpression(methodCallExpression);
public static MethodInfo GetMethod<TInput>(Expression<Action<TInput>> methodCallExpression) => GetMethodByLambdaExpression(methodCallExpression);
public static MethodInfo GetMethod<TOutput>(Expression<Func<TOutput>> methodCallExpression) => GetMethodByLambdaExpression(methodCallExpression);
public static MethodInfo GetMethod(Expression<Action> methodCallExpression) => GetMethodByLambdaExpression(methodCallExpression);
/// <summary>
/// Get the method definition by a lambda expression instead of using reflection api
/// Very helpful when the generic method has a lot of overloads, especially helpful
/// if it's a generic overloaded method
/// </summary>
/// <param name="methodCallExpression">The lambda expression that invokes the desired method</param>
/// <returns>The method information</returns>
public static MethodInfo GetMethodByLambdaExpression(LambdaExpression methodCallExpression)
{
var method = (methodCallExpression.Body as MethodCallExpression)?.Method ??
throw new ArgumentException("The provided expression doesn't invoke a method as expected",
nameof(methodCallExpression));
return method.IsConstructedGenericMethod ? method.GetGenericMethodDefinition() : method;
}
}
using System;
using Should;
using System.Linq;
using static ReflectionUtils;
private class Clazz
{
public string Method() => throw new NotImplementedException();
public void Method(string id) {}
public string Method(int id) => throw new NotImplementedException();
public void Method<T>(IdGetter<T> idGetter) {}
public string Method<T>(Func<T> idGetter) => throw new NotImplementedException();
}
private class IdGetter<T>
{
public T GetId() => throw new NotImplementedException();
}
var type = typeof(Clazz);
var klazz = new Clazz();
var methodName = nameof(Clazz.Method);
var method = GetMethod(() => new Clazz().Method());
var expectedMethod = type.GetMethod(methodName, new Type[]{});
method.ShouldEqual(expectedMethod);
var method2 = GetMethod((Clazz clazz) => clazz.Method());
method2.ShouldEqual(method);
var method3 = GetMethod((Clazz clazz) => clazz.Method("str"));
var expectedMethod3 = type.GetMethod(methodName, new []{typeof(string)});
method3.ShouldEqual(expectedMethod3);
var method4 = GetMethod(() => klazz.Method(new IdGetter<int>()));
var expectedMethod4 = type.GetMethods()
.SingleOrDefault(x => x.ContainsGenericParameters &&
x.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() == typeof(IdGetter<>));
method4.ShouldEqual(expectedMethod4);
var method5 = GetMethod(() => klazz.Method(() => true));
var expectedMethod5 = type.GetMethods()
.SingleOrDefault(x => x.ContainsGenericParameters &&
x.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() == typeof(Func<>));
method5.ShouldEqual(expectedMethod5);
// If we don't invoke method in the lambda expression, should throw ArgumentException
Assert.Throws<ArgumentException>(() => GetMethod(() => klazz == null));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment