Как получить пользовательский атрибут из экземпляра объекта в С#
Скажем, у меня есть класс Test с одним свойством Title с пользовательским атрибутом:
public class Test
{
[DatabaseField("title")]
public string Title { get; set; }
}
И метод расширения, называемый DbField. Мне интересно, возможно ли получение пользовательского атрибута из экземпляра объекта в С#.
Test t = new Test();
string fieldName = t.Title.DbField();
//fieldName will equal "title", the same name passed into the attribute above
Можно ли это сделать?
Ответы
Ответ 1
Вот подход. Метод расширения работает, но это не так просто. Я создаю выражение, а затем извлекаю пользовательский атрибут.
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; }
}
}
Ответ 2
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;
}
}
}
Ответ 3
Это, но в конечном итоге это будет окольный путь, так как вы получите экземпляр Type
от вызова GetType
на вашем экземпляре, который предоставляет свойство, а затем работает над этим (чаще всего).
В этом конкретном случае ваш метод расширения не сможет получить информацию об атрибутах, потому что все, что вы передаете ему, это строка.
В конечном итоге вам нужно что-то получить PropertyInfo
для свойства. Другие ответы относятся к Type
, чего им не хватает, это не единственный способ получить информацию об атрибутах в PropertyInfo
, которую вы хотите.
Вы можете сделать это, передав экземпляр Type
с строкой, предположительно, с именем свойства, чтобы вы могли вызвать GetProperty
на Type
.
Другой способ сделать это, поскольку С# 3.0 должен был иметь метод, который принимает Expression<T>
, а затем использовать части Expression
, чтобы попасть в PropertyInfo
. В этом случае вы должны взять Expression<Func<string>>
или что-то там, где TResult
- строка.
Как только у вас есть PropertyInfo
, вы можете вызвать GetCustomAttributes
на нем и искать свой атрибут.
Преимущество подхода выражения заключается в том, что Expression<T>
происходит от LambdaExpression
, который вы можете вызвать Compile
on, а затем вызвать, чтобы получить фактическое значение, если оно вам нужно.
Ответ 4
Нет, это невозможно. Причина этого в том, что это значение, а не само свойство, которое будет отправлено в любой пользовательский метод расширения, который будет извлекать эту информацию. Как только вы перейдете к этому методу расширения, нет надежного способа отследить его.
Возможно, будет для значений перечисления, но что касается свойств на POCO, это не сработает.
Ответ 5
Чтобы получить значение атрибута, вам нужен тип, к которому применяется атрибут. Ваш метод расширения получает только строковое значение (значение Title), поэтому вы не сможете получить фактический экземпляр, из которого получена строка, и поэтому вы не можете получить исходный тип, к которому принадлежит свойство Title. Это сделает невозможным получение значения атрибута из вашего метода расширения.
Ответ 6
Как было указано, это невозможно с синтаксисом, описанным в оригинальном плакате, потому что вы не можете получить ссылку на 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");