Lambda的属性构造函数

可以这样做:

public static void SomeMethod(Expression expr) { //LambdaExpression happily excepts any Expession LambdaExpression lamb = expr; } 

并在其他地方调用它传递参数的lambda:

 SomeMethod<Func<IQueryable,Person>>( p=>p.FirstOrDefault()); 

我想改为将表达式作为参数传递给属性构造函数可以这样做吗?

 class ExpandableQueryAttribute: Attribute { private LambdaExpression someLambda; //ctor public ExpandableQueryMethodAttribute(LambdaExpression expression) { someLambda = expression } } //usage: static LambdaExpression exp = (Expression<Func<IQueryable, Person>>) (p => p.FirstOrDefault()); [ExpandableQueryAttribute(exp)] //error here // "An attribute argument must be a constant expression, typeof expression // or array creation expression of an attribute parameter type" 

我的目标是在属性的构造函数中指定方法或lambda(即使我必须声明一个完整的命名方法并以某种方式传递方法的名称,这样就可以了)。

  1. 参数类型可以更改,但重要的是属性构造函数可以采用该参数,并以某种方式将其分配给LambdaExpression类型的字段

  2. 我希望lambda /方法的声明就在对属性构造函数或内联的调用之上,这样你就不用去看看传递的内容了。

所以这些替代方案会很好,但没有运气让他们工作:

 public static ... FuncName(...){...} [ExpandableQueryAttribute(FuncName)] // ... 

要么

 //lambdas aren't allowed inline for an attribute, as far as I know [ExpandableQueryAttribute(q => q.FirstOrDefault())] // ... 

现有的解决方法是将一个数字ID传递给构造函数(满足“参数必须是常量”的要求),构造函数使用它来在先前添加了表达式的字典中进行查找。 希望改进/简化这一点,但我感觉由于属性构造函数的限制它没有变得更好。

这个怎么样:

  class ExpandableQueryAttribute : Attribute { private LambdaExpression someLambda; //ctor public ExpandableQueryAttribute(Type hostingType, string filterMethod) { someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null); // could also use a static method } } 

这应该让你将lambda分配给一个字段,然后在运行时将其吸入,尽管通常我更喜欢使用类似PostSharp的东西在编译时执行此操作。

简单的用法示例

  public class LambdaExpressionAttribute : Attribute { public LambdaExpression MyLambda { get; private set; } //ctor public LambdaExpressionAttribute(Type hostingType, string filterMethod) { MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null); } } public class User { public bool IsAdministrator { get; set; } } public static class securityExpresions { public static readonly LambdaExpression IsAdministrator = (Expression>)(x => x.IsAdministrator); public static readonly LambdaExpression IsValid = (Expression>)(x => x != null); public static void CheckAccess(User user) { // only for this POC... never do this in shipping code System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); var method = stackTrace.GetFrame(1).GetMethod(); var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType(); foreach (var filter in filters) { if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false) { throw new UnauthorizedAccessException("user does not have access to: " + method.Name); } } } } public static class TheClass { [LambdaExpression(typeof(securityExpresions), "IsValid")] public static void ReadSomething(User user, object theThing) { securityExpresions.CheckAccess(user); Console.WriteLine("read something"); } [LambdaExpression(typeof(securityExpresions), "IsAdministrator")] public static void WriteSomething(User user, object theThing) { securityExpresions.CheckAccess(user); Console.WriteLine("wrote something"); } } static void Main(string[] args) { User u = new User(); try { TheClass.ReadSomething(u, new object()); TheClass.WriteSomething(u, new object()); } catch(Exception e) { Console.WriteLine(e); } } 

这是不可能的,因为您可以传递到属性中的内容需要适合CLR的二进制DLL格式,并且无法对任意对象初始化进行编码。 对于相同的你,你不能传递可以为例的值。 限制非常严格。

虽然您不能拥有属性的复杂构造函数,但在某些情况下,工作方法是为该属性创建公共属性并在运行时更新它。

self指向包含其属性的某些属性的类的对象。 LocalDisplayNameAttribute是一个自定义属性。

以下代码将在运行时设置我的自定义属性类的ResourceKey属性。 然后,此时您可以覆盖DisplayName以超出您想要的任何文本。

  static public void UpdateAttributes(object self) { foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(self)) { LocalDisplayNameAttribute attr = prop.Attributes[typeof(LocalDisplayNameAttribute)] as LocalDisplayNameAttribute; if (attr == null) { continue; } attr.ResourceKey = prop.Name; } } 

使用dymanic linq: http ://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

您可以使属性的构造函数采用一个求值为表达式的字符串。