2010-02-24 12 views
20

Powiedzmy mam klasy o nazwie test z jedną właściwość o nazwie tytułowy z atrybutu niestandardowego:Jak uzyskać atrybut niestandardowy z instancji obiektu w C#

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

i metodę rozszerzenia nazwie DbField. Zastanawiam się, czy uzyskanie niestandardowego atrybutu z instancji obiektu jest możliwe w języku C#.

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

Czy można to zrobić?

+0

To może być późno, ale sprawdź metodę TypeDescriptor i GetAttributes, która pobiera wystąpienie. Oto bardzo fajny post o tym samym zastosowaniu: http://geekswithblogs.net/abhijeetp/archive/2009/01/10/dynamic-attributes-in-c.aspx –

Odpowiedz

26

Oto podejście. Metoda rozszerzenia działa, ale nie jest to takie proste. Tworzę wyrażenie, a następnie odczytuję atrybut niestandardowy.

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

    } 
} 
+0

To robi Wydaje się, że działa, gdy Program.Title nie jest łańcuchem, na przykład Guid. – user2320724

+0

Podoba mi się ta odpowiedź, ale chciałbym użyć dwóch rodzajów ogólnych, aby nie były ograniczone do jednego typu, jak w problemie wspomnianym przez @ user2320724. Podpis może wyglądać jak "public static string DbField (this T obj, Expression > value)" –

2

Jest, ale docelowo będzie okrężny, ponieważ wywoła instancję Type z wywołania GetType na instancji, która eksponuje właściwość, a następnie nad nią pracuje (częściej niż nie).

W tym konkretnym przypadku, twoja metoda rozszerzenia nie będzie w stanie uzyskać informacji o atrybutach, ponieważ wszystko, co do Ciebie przekazujesz, jest ciągiem.

W końcu to, czego potrzebujesz, to coś, z czego można uzyskać PropertyInfo. Inne odpowiedzi odnoszą się do Type, czego im brakuje, to nie jest jedyny sposób, aby uzyskać informacje o atrybutach na PropertyInfo, które chcesz.

Możesz to zrobić, przekazując instancję Type ciągiem, prawdopodobnie z nazwą właściwości, aby można było wywołać GetProperty na Type.

Innym sposobem na osiągnięcie tego od C# 3.0 został mieć metodę, która zajmuje Expression<T> a następnie użyć części Expression dostać na PropertyInfo. W takim przypadku możesz wziąć Expression<Func<string>> lub coś, gdzie TResult jest łańcuchem.

Po uzyskaniu numeru PropertyInfo można zadzwonić pod numer GetCustomAttributes i poszukać atrybutu.

Zaletą metody wyrażania jest to, że Expression<T> pochodzi od LambdaExpression, którą można wywołać pod numerem Compile, a następnie wywołać, aby uzyskać aktualną wartość, jeśli jest potrzebna.

0

Nie, to niemożliwe. Powodem tego jest to, że to wartość, a nie sama właściwość, która zostanie przesłana do dowolnej niestandardowej metody rozszerzenia, która pobrałaby te informacje. Po wejściu w tę metodę rozszerzenia nie ma niezawodnego sposobu na powrót do samej właściwości.

Może być możliwe for enum values, ale jeśli chodzi o właściwości w POCO, to nie zadziała.

0

Aby uzyskać wartość atrybutu, potrzebny jest typ, którego dotyczy atrybut. Twoja metoda rozszerzenia pobiera tylko wartość ciągu (wartość tytułu), więc nie można uzyskać rzeczywistego wystąpienia, z którego pochodzi łańcuch, a więc nie można uzyskać oryginalnego typu, do którego należy właściwość Title. To uniemożliwi uzyskanie wartości atrybutu z metody rozszerzenia.

+0

@NerdFury: Zupełnie nie tak, ponieważ atrybut znajduje się na właściwość, potrzebujesz PropertyInfo, a nie typ. Ponadto użycie Type nie jest jedynym sposobem uzyskania właściwości PropertyInfo, istnieją inne sposoby, aby to zrobić .NET 3.5/C# 3.0. – casperOne

+0

Sprawiedliwe, powinienem być bardziej ostrożny z moimi słowami. Więc tak, potrzebuje właściwości PropertyInfo, aby sprawdzić niestandardowe atrybuty, ale nadal nie może uzyskać tego z metody rozszerzenia na obiekcie i wywołać go na łańcuchu. Nie ma sposobu, aby uzyskać powyższy kod, aby podać wartość atrybutu. Zgadzam się z Tobą, że dzięki Expression Trees mógł to zrobić, ale kod nie wyglądałby tak, jak chciałby wyglądać jego interfejs API. – NerdFury

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


} 
+0

Otrzyma wartość, ale nie za pomocą metody rozszerzenia, jak zaproponował pytający. – NerdFury

+0

Spójrz na. – garik

+0

Wygląda dobrze ... jedyne, czego mógłbym szukać, to jakoś uczynić to wyrażenie dynamicznym, aby mogło być używane przez wiele różnych właściwości tego obiektu: GetProperty ("Tytuł") – kabucey

0

jak wskazano, nie jest to możliwe ze składnią oryginalny plakat opisane, ponieważ nie można uzyskać odniesienie do PropertyInfo wewnątrz metody wydłużania.Co o coś takiego:

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

Następnie można uzyskać informacje, jak po prostu jako:

Test t = new Test(); 
string dbField = t.GetDbField("Title"); 
Powiązane problemy