【问题标题】:How to get a custom attribute from object instance in C#如何从 C# 中的对象实例中获取自定义属性
【发布时间】:2011-01-20 17:54:32
【问题描述】:

假设我有一个名为 Test 的类,它有一个名为 Title 的属性和一个自定义属性:

public class Test
{
    [DatabaseField("title")]
    public string Title { get; set; }
}

还有一个名为 DbField 的扩展方法。我想知道在 c# 中是否可以从对象实例中获取自定义属性。

Test t = new Test();
string fieldName = t.Title.DbField();
//fieldName will equal "title", the same name passed into the attribute above

这个可以吗?

【问题讨论】:

标签: c# .net reflection custom-attributes


【解决方案1】:

确实如此,但最终这将是一种迂回的方式,因为您将通过在暴露该属性的实例上调用 GetType 来获得 Type 实例,然后继续处理(通常情况下)。

在这种特定情况下,您的扩展方法将无法获取属性信息,因为您传递给它的只是一个字符串。

最终,您需要的是从中获取属性的PropertyInfo。其他答案是指Type,他们缺乏的是,这不是在PropertyInfo 获取您想要的属性信息的唯一方法。

您可以通过传递带有字符串的Type 实例来实现这一点,大概带有属性名称,因此您可以在Type 上调用GetProperty

自 C# 3.0 以来执行此操作的另一种方法是采用一种方法,该方法采用 Expression<T>,然后使用 Expression 的部分来获取 PropertyInfo。在这种情况下,您将采用 Expression<Func<string>>TResult 是字符串的东西。

获得PropertyInfo 后,您可以调用GetCustomAttributes 并查找您的属性。

表达式方法的优点是Expression<T> 派生自LambdaExpression,可以调用Compile on,然后调用以获取实际值,如果需要的话。

【讨论】:

    【解决方案2】:

    这是一种方法。扩展方法有效,但并不那么容易。我创建一个表达式,然后检索自定义属性。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Linq.Expressions;
    
    namespace ConsoleApplication1
    {
        public class DatabaseFieldAttribute : Attribute
        {
            public string Name { get; set; }
    
            public DatabaseFieldAttribute(string name)
            {
                this.Name = name;
            }
        }
    
        public static class MyClassExtensions
        {
            public static string DbField<T>(this T obj, Expression<Func<T, string>> value)
            {
                var memberExpression = value.Body as MemberExpression;
                var attr = memberExpression.Member.GetCustomAttributes(typeof(DatabaseFieldAttribute), true);
                return ((DatabaseFieldAttribute)attr[0]).Name;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var p = new Program();
                Console.WriteLine("DbField = '{0}'", p.DbField(v => v.Title));
    
            }
            [DatabaseField("title")]
            public string Title { get; set; }
    
        }
    }
    

    【讨论】:

    • 当 Program.Title 不是字符串(例如 Guid)时,这似乎不起作用。
    • 我喜欢这个答案,但我会使用两种泛型类型,这样您就不会像@user2320724 提到的问题那样被限制为一种类型。签名可能类似于 public static string DbField&lt;T1, T2&gt;(this T obj, Expression&lt;Func&lt;T1, T2&gt;&gt; value)
    【解决方案3】:
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Test t = new Test();
    
                Console.WriteLine(t.FieldName("Title").FieldName<DatabaseFieldAttribute>());
                Console.WriteLine(t.FieldName("Title").FieldIsPrimaryKey<DatabaseFieldAttribute>());
            }
    
    
        }
    
        public class Test
        {
            [DatabaseField("titlezzz", true)]
            public string Title
            {
                get;
                set;
            }
        }
    
    
        public class BaseDatabaseFieldAttribute : Attribute
        {
            private readonly string _name;
    
            public string Name { get { return _name; } }
    
            public BaseDatabaseFieldAttribute(string name)
            {
                _name = name;
            }
        }
        public class DatabaseFieldAttribute : BaseDatabaseFieldAttribute
        {
            private readonly bool _isPrimaryKey;
    
            public bool IsPrimaryKey { get { return _isPrimaryKey; } }
    
            public DatabaseFieldAttribute(string name, bool isPrimaryKey): base(name)
            {
                _isPrimaryKey = isPrimaryKey;
            }
        }
    
        public static class Helper
        {
    
            public static PropertyInfo FieldName(this object obj, string propertyName)
            {
                return obj.GetType().GetProperty(propertyName);
            }
    
            public static string FieldName<T>(this PropertyInfo property) where T: BaseDatabaseFieldAttribute
            {
                object[] os = property.GetCustomAttributes(typeof(T), false);
    
                if (os != null && os.Length >= 1)
                    return (os[0] as T).Name;
                else
                    return "N/A";
            }
    
            public static bool? FieldIsPrimaryKey<T>(this PropertyInfo property) where T : DatabaseFieldAttribute
            {
                object[] os = property.GetCustomAttributes(typeof(T), false);
    
                if (os != null && os.Length >= 1)
                    return (os[0] as T).IsPrimaryKey;
                else
                    return null;
            }
        }
    
    
    }
    

    【讨论】:

    • 这将获得值,但不是通过提问者提出的扩展方法。
    • 看起来不错...接下来我唯一要寻找的就是以某种方式使该语句动态化,以便该对象的许多不同属性可以使用它:GetProperty("Title")
    【解决方案4】:

    正如所指出的,使用原始海报描述的语法是不可能的,因为您无法在扩展方法中获得对 PropertyInfo 的引用。像这样的东西呢:

    // Extension method
    public static string GetDbField(this object obj, string propertyName)
    {
        PropertyInfo prop = obj.GetType().GetProperty(propertyName);
        object[] dbFieldAtts = prop.GetCustomAttributes(typeof(DatabaseFieldAttribute), true);
    
        if (dbFieldAtts != null && dbFieldAtts.Length > 0)
        {
            return ((DatabaseFieldAttribute)dbFieldAtts[0]).Name;
        }
    
        return "UNDEFINED";
    }
    

    然后您可以像这样简单地获取信息:

    Test t = new Test();
    string dbField = t.GetDbField("Title");
    

    【讨论】:

      【解决方案5】:

      为了获取属性值,您需要该属性适用的类型。您的扩展方法仅获取一个字符串值(Title 的值),因此您将无法获取该字符串来自的实际实例,因此您无法获取 Title 属性所属的原始类型。这将使您无法从您的扩展方法中获取属性值。

      【讨论】:

      • @NerdFury:完全错误,因为属性在属性上,所以您需要 PropertyInfo,而不是 Type。此外,使用 Type 并不是获取 PropertyInfo 的唯一方法,自 .NET 3.5/C# 3.0 以来还有其他方法可以做到这一点。
      • 很公平,我应该更加小心我的话。所以是的,他需要 PropertyInfo 来检查自定义属性,但他仍然无法从对象的扩展方法中获取它并在字符串上调用它。没有办法让上面写的代码给出属性值。我同意你的观点,通过 Expression Trees,他可以做到,但代码看起来不像他想要的 API 的样子。
      【解决方案6】:

      不,这是不可能的。这样做的原因是它是值,而不是属性本身,它将被发送到将获取此信息的任何自定义扩展方法中。一旦进入该扩展方法,就没有可靠的方法可以追溯到属性本身。

      for enum values 可能是可能的,但就 POCO 上的属性而言,它不会起作用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-05
        相关资源
        最近更新 更多