2010-02-24 7 views
20

Digamos que tengo una clase llamada de prueba con una propiedad llamada Título con un atributo personalizado:Cómo conseguir un atributo personalizado de instancia de objeto en C#

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

y un método de extensión llamada DbField. Me pregunto si incluso es posible obtener un atributo personalizado de una instancia de objeto en C#.

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

¿Se puede hacer esto?

+0

Esto tal vez tarde, pero echa un vistazo al método TypeDescriptor y GetAttributes que toma una instancia. Aquí hay una publicación muy buena con el mismo uso: http://geekswithblogs.net/abhijeetp/archive/2009/01/10/dynamic-attributes-in-c.aspx –

Respuesta

26

Aquí es un enfoque. El método de extensión funciona, pero no es tan fácil. Creo una expresión y luego recupero el atributo personalizado.

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

Esto no lo hace Parece que funciona cuando Program.Title no es una cadena, un Guid, por ejemplo. – user2320724

+0

Me gusta esta respuesta, pero utilizaría dos tipos genéricos para que no esté limitado a un tipo como en el problema mencionado por @ user2320724. La firma podría verse como 'cadena estática pública DbField (este T obj, Expresión > valor)' –

2

Lo es, pero en última instancia va a ser una forma indirecta, ya que obtendrá la instancia Type de llamar al GetType en su instancia que expone la propiedad, y luego trabajar en eso (la mayoría de las veces).

En este caso específico, su método de extensión no podrá obtener la información del atributo porque todo lo que le está pasando es una cadena.

En última instancia, lo que necesita es algo para obtener el PropertyInfo de la propiedad. Otras respuestas se refieren al Type, lo que les falta es que esta no es la única forma de obtener la información de los atributos en el PropertyInfo que desee.

Usted puede hacer que al hacer pasar una instancia Type con una cadena, presumiblemente, con el nombre de la propiedad, por lo que se puede llamar GetProperty en el Type.

Otra forma de hacer esto ya que C# 3.0 ha sido disponer de un método que toma un Expression<T> y luego usar las partes de la Expression para llegar a la PropertyInfo. En este caso, tomaría un Expression<Func<string>> o algo donde TResult es una cadena.

Una vez que tenga el PropertyInfo, puede llamar al GetCustomAttributes y buscar su atributo.

La ventaja del enfoque de expresión es que Expression<T> deriva de LambdaExpression, que puede llamar al Compile, y luego llamar para obtener el valor real, si lo necesita.

0

No, no es posible. La razón de esto es que es el valor, y no la propiedad en sí, lo que se enviará a cualquier método de extensión personalizado que obtenga esta información. Una vez que ingresa en ese método de extensión, no hay una forma confiable de rastrear la propiedad.

Podría ser posible for enum values, pero en cuanto a las propiedades en POCO, no va a funcionar.

0

Para obtener el valor del atributo, necesita el tipo al que se aplica el atributo. Su método de extensión solo obtiene un valor de cadena (el valor de Título), por lo que no podría obtener la instancia real de la que proviene la cadena, y por lo tanto no puede obtener el tipo original al que pertenece la propiedad Título. Esto hará que sea imposible obtener el valor del atributo de su método de extensión.

+0

@NerdFury: Completamente erróneo, ya que el atributo está en el propiedad, necesita el PropertyInfo, no el tipo. Además, usar el tipo no es la única forma de obtener PropertyInfo, hay otras maneras desde .NET 3.5/C# 3.0 para hacerlo. – casperOne

+0

es justo, debería haber sido más cuidadoso con mis palabras. Entonces, sí, necesita PropertyInfo para verificar atributos personalizados, pero todavía no puede obtenerlo de un método de extensión en el objeto y llamarlo en una cadena. No hay forma de obtener el código escrito anteriormente para dar el valor del atributo. Estoy de acuerdo contigo, que a través de Expression Trees, él podría hacerlo, pero el código no se vería como quería que fuera su 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

Esto obtendrá el valor, pero no a través de un método de extensión como propuso el asker. – NerdFury

+0

Eche un vistazo a. – garik

+0

Se ve bien ... lo único que buscaría a continuación es hacer que esta afirmación sea dinámica para que pueda ser utilizada por muchas propiedades diferentes de ese objeto: GetProperty ("Título") – kabucey

0

Como ya se ha señalado, no es posible con la sintaxis del original cartel descrito, porque no se puede obtener una referencia a PropertyInfo dentro del método de extensión.¿Qué pasa con algo como esto:

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

A continuación, se pudo obtener información tan simple como:

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