我理解lambdas Func和Action代表们.但表达方式让我很难过.在什么情况下你会使用一个Expression<Func<T>>而不是一个普通的老年人Func<T>?
通过lambda表达式传入时,是否有更好的方法来获取属性名称?这是我现在拥有的.
例如.
GetSortingInfo<User>(u => u.UserId);
Run Code Online (Sandbox Code Playgroud)
只有当属性是字符串时,它才能将其作为元素表达式进行处理.因为不是所有属性都是字符串我必须使用对象,但它会返回一个单一表达式.
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr …Run Code Online (Sandbox Code Playgroud) (参见下面我使用我接受的答案创建的解决方案)
我正在尝试提高一些涉及反射的代码的可维护性.该应用程序有一个.NET Remoting接口,公开(除此之外)一个名为Execute的方法,用于访问未包含在其已发布的远程接口中的应用程序部分.
以下是应用程序如何指定可通过Execute访问的属性(本例中为静态属性):
RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");
Run Code Online (Sandbox Code Playgroud)
所以远程用户可以调用:
string response = remoteObject.Execute("SomeSecret");
Run Code Online (Sandbox Code Playgroud)
并且应用程序将使用反射来查找SomeClass.SomeProperty并将其值作为字符串返回.
不幸的是,如果有人重命名SomeProperty并忘记更改ExposeProperty()的第3个parm,它会破坏这种机制.
我需要相当于:
SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()
Run Code Online (Sandbox Code Playgroud)
在ExposeProperty中用作第三个parm,因此重构工具将负责重命名.
有没有办法做到这一点?提前致谢.
好的,这是我最终创建的内容(根据我选择的答案和他引用的问题):
// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
var me = propertyLambda.Body as MemberExpression;
if (me == null)
{ …Run Code Online (Sandbox Code Playgroud) 使用方法调用很容易从lambda转到Expression
public void GimmeExpression(Expression<Func<T>> expression)
{
((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}
public void SomewhereElse()
{
GimmeExpression(() => thing.DoStuff());
}
Run Code Online (Sandbox Code Playgroud)
但我想将Func转换为表达式,仅在极少数情况下......
public void ContainTheDanger(Func<T> dangerousCall)
{
try
{
dangerousCall();
}
catch (Exception e)
{
// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;
var nameOfDanger =
((MemberExpression)dangerousCall.Body).Member.Name;
throw new DangerContainer(
"Danger manifested while " + nameOfDanger, e);
}
}
public void SomewhereElse()
{
ContainTheDanger(() => thing.CrossTheStreams());
}
Run Code Online (Sandbox Code Playgroud)
不起作用的行给了我编译时错误Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'.显式强制转换无法解决问题.我有一个设施可以做到这一点吗?
我正在开发一个使用lambda表达式来指定属性的API.我正在使用这个与此类似的着名代码(这是简化和不完整的,只是为了说清楚我在说什么):
public void Foo<T, P>(Expression<Func<T, P>> action)
{
var expression = (MemberExpression)action.Body;
string propertyName = expression.Member.Name;
// ...
}
Run Code Online (Sandbox Code Playgroud)
被称为这样:
Foo((String x) => x.Length);
Run Code Online (Sandbox Code Playgroud)
现在我想通过链接属性名来指定属性路径,如下所示:
Foo((MyClass x) => x.Name.Length);
Run Code Online (Sandbox Code Playgroud)
Foo应该能够将路径拆分为其属性名称("Name"和"Length").有没有办法以合理的努力做到这一点?
有一个类似的外观问题,但我认为他们正试图在那里结合lambda表达式.
另一个问题也是处理嵌套的属性名称,但我真的不明白他们在谈论什么.
鉴于
Expression<Func<T, object>>
Run Code Online (Sandbox Code Playgroud)
(例如x => x.Prop1.SubProp),我想根据需要创建一个字符串"Prop1.SubProp".
在单次访问的情况下(例如x => x.Prop1),我可以轻松地执行以下操作:
MemberExpression body = (expression.Body.NodeType == ExpressionType.Convert) ? (MemberExpression)((UnaryExpression)expression.Body).Operand : (MemberExpression)expression.Body;
return body.Member.Name;
Run Code Online (Sandbox Code Playgroud)
但是,如果存在更深的嵌套,例如x => x.Prop1.SubProp1,则只获得嵌套最深的名称,例如"SubProp1"而不是"Prop1.SubProp1"
反正有没有访问lambda表达式的完整属性路径?
我经常编写C#代码,必须使用魔术字符串来表达属性名称.每个人都知道魔术弦的问题.它们很难重构,它们没有编译时检查,并且通常会导致难以诊断的问题.然而,C#/ .NET 在整个地方使用它们来表示属性/类/方法名称.
这个问题已持续多年和多年,目前唯一可行的解决方案是使用表达式树,然后在运行时解析属性名称.这使您获得令人满意的编译时检查,但它使代码复杂化(需要Expression类型的参数),并且会产生运行时成本.
有没有人知道是否有一个特性考虑因为C#/ .NET添加编译时反射来克服这个普遍存在的问题?
看起来这将是一个简单的补充,它将是一个非破坏性的变化,它将使许多开发人员受益匪浅.typeof()运算符已经执行了编译时反射的形式,因此看起来运算符nameof()(或类似的东西)将非常互补.
此外,有没有人知道这个功能的任何潜在问题?
谢谢您的帮助.
我有一个问题,一直困扰我一段时间,我找不到答案.
我需要获取Lambda表达式中引用的属性的名称.我会将lambda表达式提供给一个返回字符串的方法.例如,如果我有:
x => x.WeirdPropertyName
Run Code Online (Sandbox Code Playgroud)
然后该方法将返回:
"WeirdPropertyName"
Run Code Online (Sandbox Code Playgroud)
我已经读过它可以用表达式树来完成,但答案已经没有了.
谢谢你的帮助
我正在尝试写一个强类型帮助器,它将是这样的:
Html.Lookup(x => x.FooId);
Run Code Online (Sandbox Code Playgroud)
现在我有这个:
public static MvcHtmlString Lookup<T,TReturn>(this HtmlHelper<T> html, Func<T, TReturn> expression)
{
// get string "FooId" here
}
Run Code Online (Sandbox Code Playgroud)
谁知道怎么弄这个?
我已经实现了一个MVC扩展来格式化我的应用程序中的数字.它基于此处的代码.如下
public static MvcHtmlString DecimalBoxFor<TModel>(this HtmlHelper<TModel> html, Expression<Func<TModel, double?>> expression, string format, object htmlAttributes = null)
{
var name = ExpressionHelper.GetExpressionText(expression);
double? dec = expression.Compile().Invoke(html.ViewData.Model);
var value = dec.HasValue ? (!string.IsNullOrEmpty(format) ? dec.Value.ToString(format) : dec.Value.ToString()): "";
return html.TextBox(name, value, htmlAttributes);
}
Run Code Online (Sandbox Code Playgroud)
当我使用以下Razor语法行调用它时
@Html.DecimalBoxFor(model => Model.PointAttributes[i].Data.Y,"0.000", new { @class = "span1 number" })
Run Code Online (Sandbox Code Playgroud)
我得到一个例外,因为我的扩展名中的变量'name'是一个空字符串.我已经尝试将var name行更改为this但它只给了我'Y'的属性名称,而不是我需要将模型绑定回MVC的完整'Model.PointAttributes [i] .Data.Y'.
var name = ((expression.Body is MemberExpression ?((MemberExpression)expression.Body).Member : ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member)).Name;
Run Code Online (Sandbox Code Playgroud) 取一个Expression树并将其转换为其他形式(例如字符串表示形式)是很常见的(例如,这个问题和这个问题,我怀疑Linq2Sql做类似的事情)。
在很多情况下,即使在大多数情况下,表达式树的转换也总是相同的,即如果我有一个函数
public string GenerateSomeSql(Expression<Func<TResult, TProperty>> expression)
Run Code Online (Sandbox Code Playgroud)
那么具有相同参数的任何调用将始终返回相同的结果,例如:
GenerateSomeSql(x => x.Age) //suppose this will always return "select Age from Person"
GenerateSomeSql(x => x.Ssn) //suppose this will always return "select Ssn from Person"
Run Code Online (Sandbox Code Playgroud)
因此,从本质上讲,带有特定参数的函数调用实际上只是一个常量,除了在运行时不断地重新计算时间而浪费时间之外。
出于争论的原因,假定转换足够复杂以至于引起明显的性能下降,是否有任何方法可以将函数调用预编译为实际常量?
编辑 似乎没有办法在C#本身中完全做到这一点。您可能会在c#中找到最接近的一个答案(当然,尽管您要确保缓存本身不比重新生成慢)。要真正转换为真正的常数,我怀疑经过一些工作,您可以在编译后使用诸如mono-cecil之类的方法来修改字节码。
是否可以指定实现以下内容:
[SomeAttribute(condition1)]
public SomeType SomeSetting1 {get; set;}
[SomeAttribute(condition2)]
public SomeType SomeSetting2 {get; set;}
Run Code Online (Sandbox Code Playgroud)
那里的条件是什么复杂的?举个例子,
[SomeAttribute(SomeSetting3 == 4 && SomeSetting4 < 100)]
Run Code Online (Sandbox Code Playgroud)
我PropertyGrid用来显示/编辑配置作为某些可序列化类的属性.我需要进行一些级联:当设置某些设置时,其他一些设置可能会被隐藏,具体取决于值.
目前,我可以通过这种方式隐藏一些设置:
IHide检查给定属性的所有属性ConfigWrapper,如果有任何IHide类型,则检查它Hide以决定何时show(添加到属性的结果集合).
public interface IHide
{
bool Hide { get; }
}
public class AdminAttribute : Attribute, Common.IHide
{
public bool Hide
{
get { return !MySettings.Admin; }
}
public override object TypeId { get { return "AdminAttributeId"; } }
} …Run Code Online (Sandbox Code Playgroud)c# ×11
lambda ×7
.net ×6
linq ×3
properties ×2
asp.net-mvc ×1
attributes ×1
compile-time ×1
delegates ×1
expression ×1
func ×1
magic-string ×1
propertygrid ×1
reflection ×1