Как получить пользовательский атрибут из экземпляра объекта в C # - PullRequest
21 голосов
/ 25 февраля 2010

Допустим, у меня есть класс с именем 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

Можно ли это сделать?

Ответы [ 6 ]

27 голосов
/ 25 февраля 2010

Вот подход. Метод расширения работает, но это не так просто. Я создаю выражение, а затем извлекаю пользовательский атрибут.

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

    }
}
6 голосов
/ 25 февраля 2010
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;
        }
    }


}
2 голосов
/ 25 февраля 2010

Да, но в конечном итоге это будет окольный путь, так как вы получите экземпляр Type от вызова GetType вашего экземпляра, который выставляет свойство, и затем будете работать над этим (чаще всего, чем нет).

В этом конкретном случае ваш метод расширения не сможет получить информацию об атрибутах, потому что все, что вы передаете ему, это строка.

В конечном счете, вам нужно получить 1007 * за собственность. Другие ответы относятся к Type, но им не хватает этого, это не единственный способ получить информацию об атрибутах на PropertyInfo, которую вы хотите.

Вы можете сделать это, передав экземпляр Type со строкой, предположительно, с именем свойства, чтобы вы могли вызвать GetProperty для Type.

Еще один способ сделать это, поскольку в C # 3.0 есть метод, который принимает Expression<T>, а затем использует части Expression, чтобы получить PropertyInfo. В этом случае вы бы взяли Expression<Func<string>> или что-то, где TResult - строка.

Если у вас есть PropertyInfo, вы можете позвонить на GetCustomAttributes и найти свой атрибут.

Преимущество подхода с использованием выражений заключается в том, что Expression<T> происходит от LambdaExpression, который можно вызвать Compile on, а затем вызвать, чтобы получить фактическое значение, если оно вам нужно.

1 голос
/ 25 февраля 2010

Как уже указывалось, это невозможно с синтаксисом, описанным в исходном плакате, потому что вы не можете получить ссылку на 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");
0 голосов
/ 25 февраля 2010

Чтобы получить значение атрибута, вам нужен тип, к которому применяется атрибут. Ваш метод расширения получает только строковое значение (значение Title), поэтому вы не сможете получить фактический экземпляр, из которого получена строка, и поэтому вы не сможете получить исходный тип, которому принадлежит свойство Title. Это сделает невозможным получение значения атрибута из вашего метода расширения.

0 голосов
/ 25 февраля 2010

Нет, это невозможно. Причина этого заключается в том, что это значение, а не само свойство, будет отправлено в любой пользовательский метод расширения, который будет извлекать эту информацию. Как только вы попадаете в этот метод расширения, нет надежного способа отследить само свойство.

Это может быть возможно для значений перечисления , но что касается свойств в POCO, это не сработает.

...