Как получить пользовательский атрибут от экземпляра объекта в C#

Скажем, у меня есть класс под названием Тест с одним свойством под названием Заголовок с пользовательским атрибутом:

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

Это может быть сделано?

23
задан abatishchev 6 July 2012 в 09:24
поделиться

6 ответов

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

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

    }
}
27
ответ дан 29 November 2019 в 02:18
поделиться

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

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

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

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

Другой способ сделать это, начиная с C # 3.0, заключался в использовании метода, который принимает Expression , а затем использует части Expression для получения ] PropertyInfo . В этом случае вы должны взять Expression > или что-то, где TResult является строкой.

Получив PropertyInfo , вы можете вызвать для него GetCustomAttributes и найти свой атрибут.

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

2
ответ дан 29 November 2019 в 02:18
поделиться

Поскольку в C++ строковые литералы (например, «Hello» не относятся к типу std:: string . Это простые массивы символов или последовательности в стиле Си.

Поэтому для строки const std:: string message = «Hello» +, «world» + exclam; компилятор должен работать со следующими типами:

const std:: строковое сообщение = const char [6] + const char [8] + std:: Последовательность;

и учитывая ассоциативность + , необходимо выполнить следующие операции:

const std:: строковое сообщение = ((const char [6] + const char [8]) + std:: последовательность);

То есть сначала должно быть вычислено самое левое сложение, а результат передан самому правому сложению.

Поэтому компилятор пытается вычислить const char [6] + const char [8] . Для массивов не определено добавление. Массивы неявно преобразуются в указатели, но это не помогает компилятору. Это означает, что в конечном итоге появится const char * + const char * , и для указателей также не определено добавление.

На данном этапе не известно, что результат должен быть преобразован в std:: Последовательностью .

Однако во втором примере

const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";

это работает, поскольку операции, которые будет видеть компилятор, std:: Последовательности + const char [8] + const char [2] . Здесь первое сложение может быть преобразовано в std:: string + const char * , и здесь оператор сложения определен и возвращает std:: string . Так что компилятор успешно вычислил первое сложение, и так как результатом была последовательность, второе сложение выглядит так: std:: Последовательности + const char [2] , и как и раньше, это невозможно, но массив можно преобразовать в указатель, и тогда компилятор сможет найти оператор сложения, который работает, снова приводя к std:: Последовательности .

-121--2549385-

От прочтения руководства здесь: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html Я думаю, что вам нужно несколько вызовов для CURL_SETOPT, первый из которых - URL, который вы хотите обработать, второй - что-то вроде:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_ptr);

Где function_ptr соответствует этой подписи:

size_t function( void *ptr, size_t size, size_t nmemb, void *stream)

Что происходит здесь, так это то, что вы обозначаете функцию обратного вызова, которую libcurl будет вызывать, когда у него есть некоторые выходные данные для записи из любой передачи вы Можно получить его для автоматической записи в файл или передать его указатель на функцию, которая будет обрабатывать сам вывод. С помощью этой функции можно собрать различные выходные последовательности в единое целое и затем использовать их в программе.

Я не уверен, какие другие параметры вам, возможно, придется установить/что еще влияет на то, как вы хотите, чтобы ваше приложение повело себя, поэтому хорошо посмотрите на эту страницу.

-121--832629-

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

Возможно для значений перечисления , но что касается свойств POCO, то они не идти на работу.

0
ответ дан 29 November 2019 в 02:18
поделиться
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;
        }
    }


}
6
ответ дан 29 November 2019 в 02:18
поделиться

Как уже было отмечено, это невозможно с синтаксисом, описанным оригинальным постером, потому что вы не можете получить ссылку на 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");
1
ответ дан 29 November 2019 в 02:18
поделиться

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

0
ответ дан 29 November 2019 в 02:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: