2013-03-25 148 views
1

我想通过使用扩展方法检查我的类成员(仅字段)上的自定义属性。使用扩展方法在字段上访问属性

public class DatabaseIdAttribute : Attribute 
{ 
    public int ID { get; set; } 

    public DatabaseIdAttribute(int id) 
    { 
     this.ID = id; 
    } 
} 

public class MyClass 
{ 
    [DatabaseId(1)] 
    double Height {get;set;} 

    [DatabaseId(2)] 
    double Width {get;set;} 

    double Area { get { return this.Height * this.Width; } 
} 

我想在扩展方法中使用LINQ表达式来访问类字段,而不是传递魔术字符串。

var myClass = new MyClass(); 
var attribute = myClass.GetAttribute<DatabaseIdAttribute>(c => c.Height); 

是否有可能实现?

[编辑]

暂且,我已经实现了与@leppie

public static MemberInfo GetMember<T, R>(this T instance, Expression<Func<T, R>> selector) 
    { 
     var member = selector.Body as MemberExpression; 
     if (member != null) 
     { 
      return member.Member; 
     } 
     return null; 
    } 

    public static T GetAttribute<T>(this MemberInfo member) where T : Attribute 
    { 
     return member.GetCustomAttributes(false).OfType<T>().SingleOrDefault(); 
    } 

这使得获取属性以下列方式

var c = new MyClass(); 
var attribute = c.GetMember(m => m.Height).GetAttribute<DatabaseIdAttribute>(); 
的帮助下

但我希望能够通过以下方式访问它:

var c = new MyClass(); 
var attribute = c.GetAttribute<DatabaseIdAttribute>(m => m.Height); 
+2

是的,这是可能的,微不足道的。你有什么尝试? – leppie 2013-03-25 05:49:16

+0

是的,它看起来微不足道,但我无法找到一个例子。这就是我在这里问的原因。 – 2013-03-25 06:06:31

+0

不要使用'object',否则会涉及转换。使用一个额外的通用参数(即'R')。此外,该成员也可以是一个'PropertyInfo'(在这种情况下)。如果你只对属性感兴趣,'MemberInfo'可能就足够了。 – leppie 2013-03-25 06:08:23

回答

4

你快到了!这应该工作(未经测试)。

public static class ObjectExtensions 
{ 
    public static MemberInfo GetMember<T,R>(this T instance, 
     Expression<Func<T, R>> selector) 
    { 
     var member = selector.Body as MemberExpression; 
     if (member != null) 
     { 
      return member.Member; 
     } 
     return null; 
    } 

    public static T GetAttribute<T>(this MemberInfo meminfo) where T : Attribute 
    { 
     return meminfo.GetCustomAttributes(typeof(T)).FirstOrDefault() as T; 
    } 
} 

用法:

var attr = someobject.GetMember(x => x.Height). 
       GetAttribute<DatabaseIdAttribute>(); 

IIRC:GetAttribute<T>(this MemberInfo meminfo)已经被定义为.NET扩展方法4.

+0

如果我没有错,我们需要三个参数来调用这个方法。我正在寻找这样的东西 var myClass = new MyClass(); var attribute = myClass.GetAttribute (c => c.Height); – 2013-03-25 07:50:36

+0

@RK:AFAIK,你是对的。你可以让它“流利”来提供帮助。将编辑答案。 – leppie 2013-03-25 07:55:05

+0

我认为这是在.NET 4.5中。但我也不确定。 – 2013-03-25 08:04:51

1

如果你不介意提供额外的泛型类型,你可以做这个:

public static class ReflectionHelper 
{ 
    public static TAttr GetAttribute<TClass, TProp, TAttr>(Expression<Func<TClass, TProp>> selector) where TAttr : Attribute 
    { 
     var member = selector.Body as MemberExpression; 
     return member.Member.GetCustomAttributes<TAttr>(false).First(); 
    } 
} 

然后你可以像这样使用它:

var attribute = ReflectionHelper.GetAttribute<MyClass, double, DatabaseIdAttribute>(m => m.Height); 

请注意,这不是一个扩展方法(因为我们不能有静态扩展),因此不需要创建类的实例。

当然,你还可以有扩展方法版本:

public static TAttr GetAttribute<TClass, TProp, TAttr>(this TClass instance, Expression<Func<TClass, TProp>> selector) where TAttr : Attribute 
{ 
    var member = selector.Body as MemberExpression; 
    return member.Member.GetCustomAttributes<TAttr>(false).First(); 
} 

这将让你怎么称呼它,像这样:

var c = new MyClass(); 
var attribute = c.GetAttribute<MyClass, double, DatabaseIdAttribute>(m => m.Height); 

但无论哪种方式相当冗长。如果你想编译推断所有的泛型类型的,我们需要在我们的属性的一个实例来传递:

public static class ReflectionHelper 
{ 
    public static TAttr GetAttribute<TClass, TProp, TAttr>(TAttr attribute, Expression<Func<TClass, TProp>> selector) where TAttr : Attribute 
    { 
     var member = selector.Body as MemberExpression; 
     return member.Member.GetCustomAttributes<TAttr>(false).First(); 
    } 
} 

用法:

var attribute = ReflectionHelper.GetAttribute(new DatabaseIdAttribute(), m => m.Height);