可以这样做:
public static void SomeMethod<TFunc>(Expression<TFunc> expr)
{
//LambdaExpression happily excepts any Expession<TFunc>
LambdaExpression lamb = expr;
}
并在其他地方调用它传递参数的lambda:
SomeMethod<Func<IQueryable<Person>,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>, 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(即使我必须声明一个完整的命名方法并以某种方式传递方法的名称,这样就可以了。)
参数类型可以更改,但重要的是属性构造函数可以采用该参数,并以某种方式将其分配给LambdaExpression类型的字段
我希望lambda /方法的声明就在对属性构造函数或内联的调用之上,这样你就不用去看看传递的内容了。
所以这些替代品会很好,但没有运气让他们工作:
public static ... FuncName(...){...}
[ExpandableQueryAttribute(FuncName)]
// ...
或
//lambdas aren't allowed inline for an attribute, as far as I know
[ExpandableQueryAttribute(q => q.FirstOrDefault())]
// ...
现有的解决方法是将数字ID传递给构造函数(满足“参数必须是常量”的要求),构造函数使用它来在先前添加了表达式的字典中进行查找。希望改进/简化这一点,但我觉得由于属性构造函数的限制,它没有变得更好。
答案 0 :(得分:7)
怎么样:
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<Predicate<User>>)(x => x.IsAdministrator);
public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(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<LambdaExpressionAttribute>();
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);
}
}
答案 1 :(得分:3)
这是不可能的,因为您可以传递到属性中的内容需要适合CLR的二进制DLL格式,并且无法对任意对象初始化进行编码。对于同样的你,你不能传递一个可以为空的值。限制非常严格。
答案 2 :(得分:0)
虽然你不能拥有复杂的属性构造函数,但在某些情况下,一个工作重点是拥有该属性的公共属性并在运行时更新它。
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;
}
}
答案 3 :(得分:-1)
您可以使属性的构造函数采用一个求值表达式的字符串。