Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error message "Operator '.' cannot be applied to operand of type 'lambda expression'" when converting a method to Extension Method?

I have a method which i want to convert to Extension Method

public static string GetMemberName<T>(Expression<Func<T>> item)
{
    return ((MemberExpression)item.Body).Member.Name;
}

and calling it like

string str = myclass.GetMemberName(() => new Foo().Bar); 

so it evaluates to str = "Bar"; // It gives the Member name and not its value

Now when i try to convert this to extension method by this

public static string GetMemberName<T>(this Expression<Func<T>> item)
{
    return ((MemberExpression)item.Body).Member.Name;
}

and call it like

string str = (() => new Foo().Bar).GetMemberName();

Error says Operator '.' cannot be applied to operand of type 'lambda expression'

Where am I wrong?

like image 687
Nikhil Agrawal Avatar asked Jun 23 '12 07:06

Nikhil Agrawal


1 Answers

To get typed expression, you will have to write it out. As others have said, there is no way compiler will automatically infer it from a lambda expression since a lambda expression can mean two things - either a delegate or an expression tree.

You can get the expression relatively simpler, by letting the compiler infer the type for you partially, like (from this answer):

public sealed class Lambda
{
    public static Func<T> Func<T>(Func<T> func)
    {
        return func;
    }

    public static Expression<Func<T>> Expression<T>(Expression<Func<T>> expression)
    {
        return expression;
    }
}

public sealed class Lambda<S>
{
    public static Func<S, T> Func<T>(Func<S, T> func)
    {
        return func;
    }

    public static Expression<Func<S, T>> Expression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }
}

//etc, to cover more cases

Call it like:

var expr1 = Lambda.Expression(() => new Foo().Bar);
var expr2 = Lambda<string>.Expression(x => x.Length); //etc

Your options are:

  1. Cast backward, to exact expression type and then call extension method on it

    var name = ((Expression<Func<BarType>>)(() => new Foo().Bar)).GetMemberName();
    

    looks ugly - cure worse than cause.

  2. Get the expression first into a variable

    Expression<Func<BarType>> expr = () => new Foo().Bar;
    var name = expr.GetMemberName();
    

    Slightly better, but still looks little off for a trivial thing.

  3. Using the Lambda classes written above

    var name = Lambda.Expression(() => new Foo().Bar).GetMemberName();
    

    Even better. It's just a little less typing.

  4. Your first pattern, which I think is the best you can get as far as readability goes

    I dont think you can improve upon that considering C# rules related to lambda expressions. That said I think few improvements can be made.

    First, extend the functionality to other lambda expression types. You would need more than Func<T> types to handle all cases. Decide what are the expression types you have to handle. For instance if you have a Func<S, T> type (as in your question - Bar on Foo), it looks better. Compare this

    myclass.GetMemberName(() => new Foo().Bar);
    

    with

    myclass.GetMemberName<Foo>(x => x.Bar);
    

    I would say these overloads would do:

    //for static methods which return void
    public static string GetMemberName(Expression<Action> expr);
    
    //for static methods which return non-void and properties and fields
    public static string GetMemberName<T>(Expression<Func<T>> expr);
    
    //for instance methods which return void
    public static string GetMemberName<T>(Expression<Action<T>> expr);
    
    //for instance methods which return non-void and properties and fields
    public static string GetMemberName<S, T>(Expression<Func<S, T>> expr);
    

    Now these can be used not just in the cases mentioned in comments, surely there are overlapping scenarios. For instance, if you already have instance of Foo, then its easier to call the second overload (Func<T>) overload for name of property Bar, like myclass.GetMemberName(() => foo.Bar).

    Secondly, implement a GetMemberName functionality common to all these overloads. An extension method on Expression<T> or LambdaExpression would do. I prefer the latter so that you get to call it even in non-strongly typed scenarios. I would write it like this, from this answer:

    public static string GetMemberName(this LambdaExpression memberSelector)
    {
        Func<Expression, string> nameSelector = null;
        nameSelector = e => //or move the entire thing to a separate recursive method
        {
            switch (e.NodeType)
            {
                case ExpressionType.Parameter:
                    return ((ParameterExpression)e).Name;
                case ExpressionType.MemberAccess:
                    return ((MemberExpression)e).Member.Name;
                case ExpressionType.Call:
                    return ((MethodCallExpression)e).Method.Name;
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    return nameSelector(((UnaryExpression)e).Operand);
                case ExpressionType.Invoke:
                    return nameSelector(((InvocationExpression)e).Expression);
                case ExpressionType.ArrayLength:
                    return "Length";
                default:
                    throw new Exception("not a proper member selector");
            }
        };
    
        return nameSelector(memberSelector.Body);
    }
    

    Lastly, GetMemberName is not a good name if you're to call it non-extension way. expression.GetMemberName() sounds more logical. Member.NameFrom<int>(x => x.ToString()) or MemberName.From<string>(x => x.Length) etc are more descriptive names for static calls.

    So overall the class might look like:

    public static class Member
    {
        public static string NameFrom(Expression<Action> expr)
        {
            return expr.GetMemberName();
        }
    
        public static string NameFrom<T>(Expression<Func<T>> expr)
        {
            return expr.GetMemberName();
        }
    
        public static string NameFrom<T>(Expression<Action<T>> expr)
        {
            return expr.GetMemberName();
        }
    
        public static string NameFrom<T>(Expression<Func<T, object>> expr)
        {
            return expr.GetMemberName();
        }
    }
    

    And usage:

    var name1 = Member.NameFrom(() => Console.WriteLine());
    var name2 = Member.NameFrom(() => Environment.ExitCode);
    var name3 = Member.NameFrom<Control>(x => x.Invoke(null));
    var name4 = Member.NameFrom<string>(x => x.Length);
    

    Most concise and clean.

  5. For properties and fields, it can be transformed to an anonymous class and then using reflection the member name can be read, as shown here.

    public static string GetMemberName<T>(T item) where T : class
    {
        if (item == null)
            return null;
    
        return typeof(T).GetProperties()[0].Name;
    }
    

    Call it like

    var name = GetMemberName(new { new Foo().Bar });
    

    It's faster, but has certain quirks, like not very refactor friendly, and doesnt help in case of methods as members. See the thread..

Of all I prefer 4.

like image 195
nawfal Avatar answered Oct 06 '22 00:10

nawfal