I want to be able to build up an expression dynamically, which is essentially a property selector.
I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.
I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.
The library takes an argument of :
Expression<Func<TObject, TPropertyType>>
An example of how this would be coded if baked into an application would be :
Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;
However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.
So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.
psuedo code :
string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];
Update :
I have got as far as this
ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);
But the problem I have with this is that the return type is not always an int but may be some other type.
Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.
public static class QueryableExtension
{
public static object Build<Tobject>(this Tobject source, string propertyName)
{
var propInfo = typeof(Tobject).GetProperty(propertyName);
var parameter = Expression.Parameter(typeof(Tobject), "x");
var property = Expression.Property(parameter, propInfo);
var delegateType = typeof(Func<,>)
.MakeGenericType(typeof(Tobject), propInfo.PropertyType);
var lambda = GetExpressionLambdaMethod()
.MakeGenericMethod(delegateType)
.Invoke(null, new object[] { property, new[] { parameter } });
return lambda;
}
public static MethodInfo GetExpressionLambdaMethod()
{
return typeof(Expression)
.GetMethods()
.Where(m => m.Name == "Lambda")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 2
&& x.Args.Length == 1
)
.Select(x => x.Method)
.First();
}
}
Usage -
var expression = testObject.Build("YourPropertyName");
Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.
As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):
static LambdaExpression MakeSelector(Type objectType, string path)
{
var item = Expression.Parameter(objectType, "item");
var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
return Expression.Lambda(body, item);
}
But then you'll need to find a way to call your generic library method - using reflection or dynamic call.
If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:
public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
string property_name)
{
var parameter = Expression.Parameter(typeof (TObject));
return Expression.Lambda<Func<TObject, TPropertyType>>(
Expression.Property(parameter, property_name), parameter);
}
There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
//...........
Expression<Func<IndexViewModel, bool>> ex =
System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
var res = ex.Compile()(model);
// res == true
System.Diagnostics.Debug.Assert(res);
Related
This seems simple, but I can't decipher the LINQ required to do it. I also can't add any new dependencies.
Basically, I'm trying to make this code generic:
if (filter.matchMode.ToUpper().Equals("EQ")) {
query = query.Where(x => x.SomeField.Equals(filter.Value));
if (filter.matchMode.ToUpper().Equals("LT")) {
query = query.Where(x => x.SomeField < filter.Value);
} else [... 5 other match modes ...]
}
Now, SomeField is only one of about 5 fields that needs this functionality. And there's 5 matching operators. I could just copy/pasta the whole thing, and then deal with the debt of having to change tons of code every time a new operator or field enters the mix, but that really doesn't seem right.
Basically, I need some way of defining SomeField at runtime, so I can factor out the whole if/else tree and use it for each supported field.
I've tried going down this road, but I think I'm misunderstanding something fundamental about expressions:
var entityType = typeof(TableObject);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression amproperty = Expression.Property(arg, "SomeField");
MemberExpression property = Expression.Property(amproperty, "Equals"); // ???
// what's next, if this is even the right direction...
EDIT: a shorter version of the question might be: how can I construct the following object foo using MemberExpressions and lambdas, such that SomeField can be passed in as a string "SomeField":
Expression<Func<TableObject, bool>> foo = x => x.SomeField.Equals("FOO");
UPDATE: here's what I ended up coming up with:
private IQueryable<TableObject> processFilter(
IQueryable<TableObject> query,
FilterItem filter,
string fieldName)
{
var entityType = typeof(TableObject);
// construct the argument and access object
var propertyInfo = entityType.GetProperty(fieldName);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression access = Expression.MakeMemberAccess(arg,
typeof(TableObject).GetProperty(fieldName)
);
// translate the operation into the appropriate Expression method
Expression oprFunc;
if (filter.MatchMode.ToUpper().Equals("EQ")) {
oprfunc =
Expression.Equal(access, Expression.Constant(filter.Value));
} else if (filter.MatchMode.ToUpper().Equals("LT")) {
oprfunc =
Expression.LessThan(access, Expression.Constant(filter.IntValue));
} else {
throw new ArgumentException(
$"invalid argument: ${filter.MatchMode}"
);
}
// construct the lambda
var func = Expression.Lambda<Func<TableObject, bool>>(oprFunc, arg);
// return the new query
return query.Where(func);
}
So far, this seems to cover most of the cases. It starts to go off the rails with nullable fields and date comparisons, but it will work for what I need it to do. The part I'm still not completely sure about is Expression.MakeMemberAccess. I've seen this written many ways and I'm not sure if that's the correct way to create that expression.
Let's say you have class like this:
class SomeClass
{
public string a1 { get; set; }
public string b1 { get; set; }
public string c1 { get; set; }
}
also let's assume you have method like this:
public List<T> GetAll(Expression<Func<T, bool>> criteria )
{
return someDataLINQCapable.Where(criteria);
}
Method could be called like this:
GetAll<SomeClass>(m => m.a1 != null && m.b1=="SomethingUseful")
I have a sorting/filtering class that takes an IEnumerable<T> for data and a object that contains the sorting/filtering conditions.
I am building expressions to then execute over the collection.
ParameterExpression parameter = Expression.Parameter(typeof(T));
Expression left = splitFilter.Aggregate((Expression)parameter, Expression.PropertyOrField);
var constant = Expression.Constant(filter.Value.Text.ToLower(), typeof(string));
var body = Expression.Call(MakeString(left, constant), "Contains", Type.EmptyTypes, constant);
Expression bodyNullCheck = CheckForNullableParameters(left, body);
Expression<Func<T, bool>> ex = Expression.Lambda<Func<T, bool>>(bodyNullCheck, parameter);
Example data structure:
public class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public List<StoreItem> BoughtItems {get; set;}
}
public class StoreItem
{
public int Id {get; set;}
public string Name {get; set;}
}
The solution works great for things that have a depth of 1 for example:
Here we can easily retrieve all Customers that have "t" in their name.
The problem comes when we wish to get all customers which have bought an item which contains "Bag" in the name.
From the front-end, I'm currently getting "BoughtItems.Name" but I can't seem to get into the "StoreItem" object even though I have the exact property name.
string[] splitFilter = filter.Key.Split('.'); // Contains {"BoughtItems", "Name"}
Expression parent = new string[] { splitFilter[0] }.Aggregate((Expression)parameter, Expression.PropertyOrField);
Expression child = new string[] { splitFilter[1] }.Aggregate(parent, Expression.PropertyOrField);
Currently, I'm trying to get the expressions for both objects separately and then to somehow combine them, but if I try to get the type of the first expression it says it can't access property Name of type Collection. (duuh)
Is there a way in C# to say "The items inside this Generic collection have this property, now using this property let's go down 1 layer and get another property?" (StoreItem.Name)
The result I'm trying to get should be semi-generic and give the same result as the example below, without using reflection.
string searchBy = "test";
var result = MyCustomers.Where(x => x.BoughtItems.Any(y => y.Name.Contains(searchBy))).ToList()
As a personal preference, I like explicit over implicit. So unless you have an un-maintainable amount of different cases coming from front-end, you should implement each case explicitly.
Let us get back to the question at hand.
You would like to achieve the following expression tree from my understanding:
Expression<Func<Customer, bool>> exp =
myCustomer => myCustomer.BoughtItems.Any(storeItem => storeItem.Name.Contains("test"));
I like commenting my code extensively so from here on most of the time comments will do the explaining :)
var myCustomerParameter = Expression.Parameter(typeof(Customer), "myCustomer");
var storeItemParameter = Expression.Parameter(typeof(StoreItem), "storeItem");
// This expression tree creates the following lambda
// storeItem => storeItem.Name.Contains("test")
var innerLambda = Expression.Lambda<Func<StoreItem, bool>>(
Expression.Call(
// storeItem.Name
Expression.Property(storeItemParameter, "Name"),
// storeItem.Name.Contains
MethodSelectorExtensions.MethodSelector<string>("Contains", new[] {typeof(string)}),
// storeItem.Name.Contains("test")
Expression.Constant("test", typeof(string))
),
storeItemParameter
);
// This expression tree creates the following lambda
// myCustomer => Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test")
var finalLambda = Expression.Lambda<Func<Customer, bool>>(
Expression.Call(
// Since LINQ is extensions methods, it is static method, no instance is required
null,
// Enumerable.Any
MethodSelectorExtensions.LinqMethodSelector("Any", new[] {typeof(IEnumerable<StoreItem>), typeof(Func<StoreItem, bool>)}),
// Enumerable.Any(myCustomer.BoughtItems, storeItem => storeItem.Name.Contains("test"))
Expression.Property(myCustomerParameter, "BoughtItems"), innerLambda
),
myCustomerParameter
);
Utility methods for creating Expressions
public static class MethodSelectorExtensions {
/// <summary>
/// Cached delegate to be used in typeof(Enumerable) methods
/// </summary>
private static readonly Func<string, Type[], MethodInfo> EnumerableMethodSelector;
/// <summary>
/// Static ctor to cache EnumerableMethodSelector function
/// </summary>
static MethodSelectorExtensions() {
var genericMethodSelector = typeof(MethodSelectorExtensions).GetMethod(nameof(MethodSelector), new[] {typeof(string), typeof(Type[])});
// C# does not allow for static type generic methods but we can create through type system ;)
EnumerableMethodSelector = (Func<string, Type[], MethodInfo>) genericMethodSelector.MakeGenericMethod(typeof(Enumerable)).CreateDelegate(typeof(Func<string, Type[], MethodInfo>));
}
public static MethodInfo MethodSelector<TType>(string nameOfMethod, Type[] functionParameterTypes) {
return typeof(TType)
.GetMethods()
.Where(n => n.Name == nameOfMethod)
// in case we have TType as Enumerable, we will need to extract generic parameter
.Select(n => n.IsGenericMethod ? n.MakeGenericMethod(functionParameterTypes[0].GenericTypeArguments[0]) : n)
.Single(n => n.GetParameters().Select(p => p.ParameterType).SequenceEqual(functionParameterTypes));
}
public static MethodInfo LinqMethodSelector(string nameOfMethod, Type[] functionParameterTypes) {
return EnumerableMethodSelector(nameOfMethod, functionParameterTypes);
}
}
And let us have a final look at the running program:
I have several autocomplete actions, one of them is listed below. Instead of writing different predicates for each autocomplete Where method, I have created an autoCompletePredicate. Since I have multiple autocompletes I am using Reflection to get the Property which is required for that specific AutoComplete and use that Property in my autoCompletePredicate.
I have following code which is working alright.
static string param1, param2;
static PropertyInfo[] properties;
static PropertyInfo prop1, prop2;
public IHttpActionResult GetAutComplete(string term, string dependent)
{
int pagerSize = 10;
properties = new MyObject().GetType().GetProperties();
prop1 = properties.Where(p => p.Name.ToUpper().Equals("PROP1")).FirstOrDefault();
prop2 = properties.Where(p => p.Name.ToUpper().Equals("PROP2")).FirstOrDefault();
param1 = term;
param2 = dependent;
return Json(context.MyObject.Where(autoCompletePredicate).Select(r => new { label = r.PROP1 }).Distinct().OrderBy(r => r.label).Take(pagerSize).ToList());
}
Func<MyObject, int, bool> autoCompletePredicate = (GF, index) =>
{
bool isFound = false;
string term, dependent;
term = prop1.GetValue(GF).ToString();
dependent = prop2.GetValue(GF).ToString();
var termFound = term.Contains(param1.ToUpper());
var dependentFound = String.IsNullOrEmpty(param2) ? true : dependent.Contains(param2.ToUpper());
isFound = termFound && dependentFound;
return isFound;
};
How can I change this code into Expression. I tried below code which compiled fine but at runtime I got the following error
public static Expression<Func<MyObject, bool>> AutoCompleteExpression()
{
return r => prop1.GetValue(r).ToString().Contains(param1.ToUpper()) && (String.IsNullOrEmpty(param2) ? true : prop2.GetValue(r).ToString().Contains(param2.ToUpper()));
}
"LINQ to Entities does not recognize the method 'System.Object
GetValue(System.Object)' method, and this method cannot be translated
into a store expression."
I looked at the following post which makes absolute sense, but I am not sure how I can use that example in my scenario (which is dynamically finding properties using Reflection).
Also, what I would like to know what can be advantage of using Expression vs Func (specially in my case)
You are trying to trying to execute Contains method on string and want that to be represented in the ExpressionTrees, following is the code you need:
Create String Extension method - Contains: (Case Insensitive)
public static class StringExtensions
{
public static bool Contains(this string source, string toCheck)
{
return source.IndexOf(toCheck, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
Create the AutoCompleteExpression method as follows, it returns Func<MyObject, bool>:
public static Func<MyObject, bool> AutoCompleteExpression()
{
// Create ParameterExpression
ParameterExpression parameterType = Expression.Parameter(typeof(MyObject), "object");
// Create MemberExpression for Columns
MemberExpression typeColumnProp1 = Expression.Property(parameterType, "PROP1");
MemberExpression typeColumnProp2 = Expression.Property(parameterType, "PROP2");
// Create MethoIndo
MethodInfo containsMethodInfo = typeof(StringExtensions).GetMethod("Contains",new[] { typeof(string), typeof(string) },null);
// Create ConstantExpression values
ConstantExpression constant1 = Expression.Constant(param1, typeof(string));
ConstantExpression constant2 = Expression.Constant(param2, typeof(string));
// Expression for calling methods
MethodCallExpression expression1 = Expression.Call(null, containsMethodInfo, typeColumnProp1, constant1);
MethodCallExpression expression2 = Expression.Call(null, containsMethodInfo, typeColumnProp2, constant2);
// Combine `MethodCallExpression` to create Binary Expression
BinaryExpression resultExpression = Expression.And(expression1,expression2);
// Compile Expression tree to fetch `Func<MyObject, bool>`
return Expression.Lambda<Func<MyObject, bool>>(resultExpression, parameterType).Compile();
}
It is possible to add lot more flexibility by defining custom extension methods and combining expressions using And / Or
I have a property whose name is in a variable (userFilters.property), which can be of type string/enum/number.
I want to select the distinct values of that property dynamically. how do I do it.
var query = CreatePincodeQuery(useFilters);
//userFilters.property contains the property to be selected
Expression<Func<PincodeData, string>> selectExpr = null;
IList<string> list = query.Select(selectExpr)
.Distinct()
.OrderBy(selectExpr)
.ToList();
return list.;
I should be creating the expression of type Expression<Func<PincodeData, string>> selectExpr to use as part of select & orderBy block.
How do I do it?
I looked at the solution provided here, here and here, but not able to understand how to I modify those to fit my need.
EDIT
came up with below solution, as expected its not working, and how do i convert the value to string.
Func<TEntity, string> CreateNewStatement<TEntity>(string field)
{
//https://stackoverflow.com/questions/16516971/linq-dynamic-select
var xParameter = Expression.Parameter(typeof(TEntity), "o");
var property = Expression.Property(xParameter, typeof(TEntity).GetProperty(field));
var lambda = Expression.Lambda<Func<TEntity, string>>(property, xParameter);
return lambda.Compile();
}
EDIT
I changed the type from string to object, but its still failing while creating the lambda for enum types, what could be the reason
In the last case, when you changed the return type to object, you didn't convert the value type to it.
That mistake is rather common because we, C# developers, writing our own C# code, get used to relying on the C# compiler to make the value type to object conversion automatically. But, that's not the case for expressions, so you need to do the conversion explicitly.
Your particular case is solved below, but! It is very, very easy. Since you are on the way to dynamically building queries based on the end-user input your next steps might become complicated very fast (for instance, filtering with logical conditions). Once that happens, consider using Dynamic LINQ, which is very powerful for such tasks.
public class Entity
{
public string StringProp { get; }
public int IntProp { get; }
public DayOfWeek EnumProp { get; }
public Entity(string stringProp, int intProp, DayOfWeek enumProp)
{
StringProp = stringProp;
IntProp = intProp;
EnumProp = enumProp;
}
}
[Test]
public void Expressions()
{
var entities = new List<Entity>
{
new("Prop3", 3, DayOfWeek.Wednesday),
new("Prop2", 2, DayOfWeek.Tuesday),
new("Prop1", 1, DayOfWeek.Monday),
};
const string stringPropName = nameof(Entity.StringProp);
var stringStatement = CreateNewStatement<Entity>(stringPropName);
const string intPropName = nameof(Entity.IntProp);
var intStatement = CreateNewStatement<Entity>(intPropName);
const string enumPropName = nameof(Entity.EnumProp);
var enumStatement = CreateNewStatement<Entity>(enumPropName);
IList<object> listOfStrings = entities.Select(stringStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfStrings));
IList<object> listOfInts = entities.Select(intStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfInts));
IList<object> listOfEnums = entities.Select(enumStatement).Distinct().OrderBy(i => i).ToList();
TestContext.WriteLine(string.Join(",", listOfEnums));
}
private static Func<TEntity, object> CreateNewStatement<TEntity>(string fieldName)
{
var parameter = Expression.Parameter(typeof(TEntity), "o");
var propertyInfo = typeof(TEntity).GetProperty(fieldName);
if (propertyInfo == null)
throw new InvalidOperationException($"Property '{fieldName}' not found");
Expression body = Expression.Property(parameter, propertyInfo);
if (propertyInfo.PropertyType.IsValueType)
body = Expression.Convert(body, typeof(object));
var lambda = Expression.Lambda<Func<TEntity, object>>(body, parameter);
return lambda.Compile();
}
I know there are a few answers on the site on this and i apologize if this is in any way duplicate, but all of the ones I found does not do what I am trying to do.
I am trying to specify method info so I can get the name in a type safe way by not using strings.
So I am trying to extract it with an expression.
Say I want to get the name of a method in this interface:
public interface IMyInteface
{
void DoSomething(string param1, string param2);
}
Currently I can get the name using THIS method:
MemberInfo GetMethodInfo<T>(Expression<Action<T>> expression)
{
return ((MethodCallExpression)expression.Body).Method;
}
I can call the helper method as follows:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething(null, null));
Console.WriteLine(methodInfo.Name);
But I am looking for the version that I can get the method name without specifying the parameters (null, null)
like this:
var methodInfo = GetMethodInfo<IMyInteface>(x => x.DoSomething);
But all attempts fail to compile
Is there a way to do this?
x => x.DoSomething
In order to make this compilable I see only two ways:
Go non-generic way and specify it's parameter as Action<string, string>
Specify Action<string, string> as your target delegate type by yourself: GetMethodInfo<IMyInteface>(x => new Action<string,string>(x.DoSomething))
if you are ok to go with second one, which allows you to omit arguments then you can write your GetMethodInfo method as follows:
MemberInfo GetMethodInfo<T>(Expression<Func<T, Delegate>> expression)
{
var unaryExpression = (UnaryExpression) expression.Body;
var methodCallExpression = (MethodCallExpression) unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression) methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo) methodInfoExpression.Value;
return methodInfo;
}
It works for your interface, but probably some generalization will be required to make this working with any method, that's up to you.
The following is compatible with .NET 4.5:
public static string MethodName(LambdaExpression expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodCallObject = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)methodCallObject.Value;
return methodInfo.Name;
}
You can use it with expressions like x => x.DoSomething, however it would require some wrapping into generic methods for different types of methods.
Here is a backwards-compatible version:
private static bool IsNET45 = Type.GetType("System.Reflection.ReflectionContext", false) != null;
public static string MethodName(LambdaExpression expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
if (IsNET45)
{
var methodCallObject = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)methodCallObject.Value;
return methodInfo.Name;
}
else
{
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo)methodInfoExpression.Value;
return methodInfo.Name;
}
}
Check this sample code on Ideone.
Note, that Ideone does not have .NET 4.5.
The problem with this is that x.DoSomething represents a method group. And you have to somehow explicitly specify what delegate type do you want to convert that method group into, so that the correct member of the group can be selected. And it doesn't matter if that group contains only one member.
The compiler could infer that you mean that one, but it doesn't do that. (I think it's this way so that your code won't break if you add another overload of that method.)
Snowbear's answer contains good advice on possible solutions.
This is a new answer to an old question, but responds to the "verbose" complaint of the accepted answer. It requires more code, but the result is a syntax like:
MemberInfo info = GetActionInfo<IMyInterface, string, string>(x => x.DoSomething);
or, for methods with a return value
MemberInfo info = GetFuncInfo<IMyInterface, object, string, string>(x => x.DoSomethingWithReturn);
where
object DoSomethingWithReturn(string param1, string param2);
Just like the framework provides Action<> and Func<> delegates up to 16 parameters, you have to have GetActionInfo and GetFuncInfo methods that accept up to 16 parameters (or more, although I'd think refactoring is wise if you have methods with 16 parameters). A lot more code, but an improvement in the syntax.
If you are ok with using the nameof() operator you can use the following approach.
One of the benefits is not having to unwrap an expression tree or supply default values or worry about having a non-null instance of the type with the method.
// As extension method
public static string GetMethodName<T>(this T instance, Func<T, string> nameofMethod) where T : class
{
return nameofMethod(instance);
}
// As static method
public static string GetMethodName<T>(Func<T, string> nameofMethod) where T : class
{
return nameofMethod(default);
}
Usage:
public class Car
{
public void Drive() { }
}
var car = new Car();
string methodName1 = car.GetMethodName(c => nameof(c.Drive));
var nullCar = new Car();
string methodName2 = nullCar.GetMethodName(c => nameof(c.Drive));
string methodName3 = GetMethodName<Car>(c => nameof(c.Drive));
If your application would allow a dependency on Moq (or a similar library), you could do something like this:
class Program
{
static void Main(string[] args)
{
var methodName = GetMethodName<IMyInteface>(x => new Action<string,string>(x.DoSomething));
Console.WriteLine(methodName);
}
static string GetMethodName<T>(Func<T, Delegate> func) where T : class
{
// http://code.google.com/p/moq/
var moq = new Mock<T>();
var del = func.Invoke(moq.Object);
return del.Method.Name;
}
}
public interface IMyInteface
{
void DoSomething(string param1, string param2);
}