简体   繁体   English

如何从C#中的对象实例获取自定义属性

[英]How to get a custom attribute from object instance in C#

Let's say I have a class called Test with one property called Title with a custom attribute: 假设我有一个名为Test的类,其一个名为Title的属性带有一个自定义属性:

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

And an extension method called DbField. 还有一个扩展方法称为DbField。 I am wondering if getting a custom attribute from an object instance is even possible in c#. 我想知道是否甚至可以在c#中从对象实例获取自定义属性。

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

Can this be done? 能做到吗?

Here is an approach. 这是一种方法。 The extension method works, but it's not quite as easy. 扩展方法可以工作,但并不是那么容易。 I create an expression and then retrieve the custom attribute. 我创建一个表达式,然后检索自定义属性。

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; }

    }
}
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;
        }
    }


}

It is, but ultimately it's going to be a roundabout way, since you will get the Type instance from calling GetType on your instance that exposes the property, and then work on that(more often than not). 是的,但是最终这将是一种回旋的方式,因为您将通过在公开该属性的实例上调用GetType来获取Type实例,然后对其进行处理(通常不这样做)。

In this specific case, your extension method isn't going to be able to get the attribute information because all you are passing to it is a string. 在这种情况下,您的扩展方法将无法获取属性信息,因为您要传递给它的只是一个字符串。

Ultimately, what you need is something to get the PropertyInfo for the property from. 最终,您需要从中获取PropertyInfo东西。 Other answers are referring to the Type , what they lack is, this is not the only way to get the attribute information at the PropertyInfo which you want. 其他答案都指向Type ,它们缺少的是,这不是在想要的PropertyInfo处获取属性信息的唯一方法。

You can do that by passing a Type instance with a string, presumably, with the property name, so you can call GetProperty on the Type . 您可以通过向Type实例传递一个大概带有属性名称的字符串来实现,因此您可以在Type上调用GetProperty

Another way of doing this since C# 3.0 has been to have a method that takes an Expression<T> and then use the parts of the Expression to get at the PropertyInfo . 自C#3.0起,执行此操作的另一种方法是拥有一个使用Expression<T> ,然后使用Expression的各个部分来获取PropertyInfo In this case, you would take an Expression<Func<string>> or something where TResult is string. 在这种情况下,您将使用Expression<Func<string>>TResult为string的内容。

Once you have the PropertyInfo , you can call GetCustomAttributes on it, and look for your attribute. 一旦有了PropertyInfo ,就可以在其上调用GetCustomAttributes并查找您的属性。

The advantage to the expression approach is that Expression<T> derives from LambdaExpression , which you can call Compile on, and then call to get the actual value, if you need it. 表达式方法的优点是Expression<T>源自LambdaExpression ,您可以对其调用Compile ,然后在需要时调用以获得实际值。

As as been pointed out, it's not possible with the syntax the original poster described, because you can't get a reference to PropertyInfo inside the extension method. 正如已经指出的那样,使用原始发布者描述的语法是不可能的,因为您无法在扩展方法内获得对PropertyInfo的引用。 What about something like this: 像这样的事情呢:

// 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";
}

You could then get the info as simply as: 然后,您可以简单地获取信息:

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

No, it's not possible. 不,不可能。 The reason for this is that it's the value, and not the property itself that would be sent into any custom extension method that would fetch this information. 这样做的原因是,将值发送给获取此信息的任何自定义扩展方法,而不是属性本身。 Once you get into that extension method, there's no reliable way to trace back to the property itself. 一旦进入该扩展方法,就没有可靠的方法可以追溯到属性本身。

It might be possible for enum values , but as far as properties on POCO's, it's not going to work. 枚举值可能是可能 ,但是就POCO的属性而言,它是行不通的。

In order to get the attribute value, you need the type that the attribute applies to. 为了获得属性值,您需要属性适用的类型。 Your extension method is only getting a string value (the value of Title), so you would not be able to get the actual instance that the string came from, and so you can't get the original type that the Title property belongs to. 您的扩展方法仅获得一个字符串值(Title的值),因此您将无法获取该字符串来自的实际实例,因此也无法获得Title属性所属的原始类型。 This will make it impossible to get the attribute value from your extension method. 这将使得无法从扩展方法中获取属性值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM