错误消息“操作员”。 在将方法转换为扩展方法时,不能应用于’lambda expression’类型的操作数?
我有一个方法,我想转换为扩展方法
public static string GetMemberName(Expression<Func> item) { return ((MemberExpression)item.Body).Member.Name; }
并称之为
string str = myclass.GetMemberName(() => new Foo().Bar);
所以它评估为str = "Bar"; // It gives the Member name and not its value
str = "Bar"; // It gives the Member name and not its value
现在,当我尝试将此转换为扩展方法时
public static string GetMemberName(this Expression<Func> item) { return ((MemberExpression)item.Body).Member.Name; }
并称之为
string str = (() => new Foo().Bar).GetMemberName();
错误说Operator '.' cannot be applied to operand of type 'lambda expression'
Operator '.' cannot be applied to operand of type 'lambda expression'
我哪里错了?
这里有两件事,第一,传递() => new Foo().Bar
接受Expression
将指定的表达式树视为Expression
,但是() => new Foo().Bar
本身不是Expression
。
其次,为了让你的扩展方法接受任何lambda(例如你提供的),你必须使用与任何表达式树相对应的类型。 但是,正如你可能已经猜到的那样,基于消息... to operand of type 'lambda expression'
,你通常会在引号中看到类型的名称,lambda表达式是由语言专门处理的,你想要做的事情,不先铸造,不可能。
以扩展方法forms调用扩展方法的方法是(在Bar
是string
类型的情况下)
((Expression>)(() => new Foo().Bar)).GetMemberName()`
这似乎并不是那么令人满意。
我哪里错了?
编译器正在告诉你究竟出了什么问题 – 你无法使用.
在lambda表达式上。
lambda表达式没有任何特定类型 – 它只能转换为表达式树。
成员访问表达式(您正在尝试执行的操作)仅在表单中可用
primary-expression . identifier type-argument-list(opt) predefined-type . identifier type-argument-list(opt) qualified-alias-member . identifier type-argument-list(opt)
……并且lambda表达式不是主要表达式。
有趣的是,这个参数不适用于匿名方法表达式,但是对于你仍然不能使用成员访问表达式。 C#规范的第7.6.4节列出了如何绑定成员访问表达式,并且大部分选项位于“如果E是预定义类型或归类为类型的主表达式 ”下(不适用)匿名方法)或“如果E是属性访问,变量或值,其类型为T” – 但匿名方法是匿名函数,并按照第7.15节:“匿名函数没有值或键入其自身“。
编辑:你仍然可以在表达式树上使用扩展方法,你不能直接在lambda表达式上使用它们。 这样可行:
Expression> expr = () => new Foo().Bar; string name = expr.GetMemberName();
……但它显然不那么有用。 (根据mlorbetske的回答,与演员同上。)
要获得类型化表达式,您必须将其写出来。 正如其他人所说,编译器无法从lambda表达式自动推断它,因为lambda表达式可能意味着两件事 – 委托或表达式树。
通过让编译器为您部分推断出类型,可以使表达式相对简单,例如( 从这个答案 ):
public sealed class Lambda { public static Func Func (Func func) { return func; } public static Expression> Expression (Expression> expression) { return expression; } } public sealed class Lambda { public static Func Func(Func func) { return func; } public static Expression> Expression (Expression> expression) { return expression; } } //etc, to cover more cases
称之为:
var expr1 = Lambda.Expression(() => new Foo().Bar); var expr2 = Lambda.Expression(x => x.Length); //etc
你的选择是:
-
向后投射到精确的表达式类型,然后调用它的扩展方法
var name = ((Expression
>)(() => new Foo().Bar)).GetMemberName(); 看起来丑陋 – 治愈比原因更糟糕。
-
首先将表达式放入变量中
Expression
> expr = () => new Foo().Bar; var name = expr.GetMemberName(); 稍微好一点,但仍然看起来很少有一件小事。
-
使用上面写的
Lambda
类var name = Lambda.Expression(() => new Foo().Bar).GetMemberName();
更好。 这只是一点点打字。
-
你的第一个模式,我认为这是你可以获得的最好的可读性
考虑到与lambda表达式相关的C#规则,我认为你不能改进。 也就是说,我认为可以做出一些改进。
首先,将function扩展到其他lambda表达式类型。 您需要的不仅仅是
Func
类型来处理所有情况。 确定您必须处理的表达式类型。 例如,如果你有一个Func
类型(如你的问题 –Foo
Bar
),它看起来更好。 比较一下myclass.GetMemberName(() => new Foo().Bar);
同
myclass.GetMemberName
(x => x.Bar); 我会说这些重载会做:
//for static methods which return void public static string GetMemberName(Expression
expr); //for static methods which return non-void and properties and fields public static string GetMemberName (Expression > expr); //for instance methods which return void public static string GetMemberName (Expression > expr); //for instance methods which return non-void and properties and fields public static string GetMemberName (Expression> expr); 现在这些不仅可以在评论中提到的情况下使用,当然还有重叠的场景。 例如,如果您已经有
Foo
实例,Foo
容易为属性Bar
名称调用第二个重载(Func
)重载,如myclass.GetMemberName(() => foo.Bar)
。其次,实现所有这些重载共有的
GetMemberName
function。Expression
或LambdaExpression
的扩展方法可以。 我更喜欢后者,所以你甚至可以在非强类型场景中调用它。 我会这样写, 从这个回答 :public static string GetMemberName(this LambdaExpression memberSelector) { Func
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); } 最后,如果您要将其称为非扩展方式,则
GetMemberName
不是一个好名称。expression.GetMemberName()
听起来更合乎逻辑。Member.NameFrom
或(x => x.ToString()) MemberName.From
等是静态调用的更具描述性的名称。(x => x.Length) 所以整体课程可能如下:
public static class Member { public static string NameFrom(Expression
expr) { return expr.GetMemberName(); } public static string NameFrom (Expression > expr) { return expr.GetMemberName(); } public static string NameFrom (Expression > expr) { return expr.GetMemberName(); } public static string NameFrom (Expression > expr) { return expr.GetMemberName(); } } 用法:
var name1 = Member.NameFrom(() => Console.WriteLine()); var name2 = Member.NameFrom(() => Environment.ExitCode); var name3 = Member.NameFrom
(x => x.Invoke(null)); var name4 = Member.NameFrom (x => x.Length); 最简洁干净。
-
对于属性和字段,可以将其转换为匿名类,然后使用reflection可以读取成员名称, 如此处所示。
public static string GetMemberName
(T item) where T : class { if (item == null) return null; return typeof(T).GetProperties()[0].Name; } 称之为
var name = GetMemberName(new { new Foo().Bar });
它更快,但有一些怪癖,如不是非常重构友好,并且在方法作为成员的情况下没有帮助。 看线程..
我更喜欢4。