web-dev-qa-db-fra.com

Comment obtenir un attribut personnalisé à partir d'une instance d'objet en C #

Disons que j'ai une classe appelée Test avec une propriété appelée Titre avec un attribut personnalisé:

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

Et une méthode d'extension appelée DbField. Je me demande si l'obtention d'un attribut personnalisé à partir d'une instance d'objet est même possible en c #.

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

Cela peut-il être fait?

21
kabucey

Voici une approche. La méthode d'extension fonctionne, mais ce n'est pas aussi facile. Je crée une expression, puis récupère l'attribut personnalisé.

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
Nick Randell
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
garik

C’est le cas, mais au final, ce sera une façon détournée, puisque vous obtiendrez l’instance Type en appelant GetType sur votre instance qui expose la propriété, puis vous y travaillerez (le plus souvent).

Dans ce cas particulier, votre méthode d'extension ne pourra pas obtenir les informations d'attribut, car tout ce que vous lui transmettez est une chaîne.

En fin de compte, vous avez besoin de quelque chose pour obtenir la PropertyInfo de la propriété. D'autres réponses font référence à la Type, ce qui leur manque, ce n'est pas le seul moyen d'obtenir les informations d'attribut à la PropertyInfo que vous voulez. 

Vous pouvez le faire en passant une instance Type avec une chaîne, vraisemblablement, avec le nom de la propriété, afin que vous puissiez appeler GetProperty sur la Type

Une autre façon de faire cela depuis C # 3.0 a été d’avoir une méthode qui prend un Expression<T> puis d’utiliser les parties de la Expression pour obtenir la PropertyInfo. Dans ce cas, vous utiliseriez un Expression<Func<string>> ou quelque chose où TResult est une chaîne. 

Une fois que vous avez la PropertyInfo, vous pouvez y appeler GetCustomAttributes et rechercher votre attribut.

L'avantage de l'approche expression est que Expression<T> dérive de LambdaExpression, que vous pouvez appeler Compile on, puis appeler pour obtenir la valeur réelle, si vous en avez besoin. 

2
casperOne

Comme cela a été souligné, la syntaxe de l'affiche originale décrite n'est pas possible car il est impossible d'obtenir une référence à PropertyInfo dans la méthode d'extension. Qu'en est-il quelque chose comme ça:

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

Vous pouvez alors obtenir les informations aussi simplement que:

Test t = new Test();
string dbField = t.GetDbField("Title");
1
WayneC

Non, ce n'est pas possible La raison en est que c'est la valeur, et non la propriété elle-même, qui serait envoyée à une méthode d'extension personnalisée qui irait chercher cette information. Une fois que vous entrez dans cette méthode d'extension, il n'y a pas de moyen fiable de remonter à la propriété elle-même. 

Cela peut être possible pour les valeurs enum , mais en ce qui concerne les propriétés sur les POCO, cela ne fonctionnera pas. 

0
David Morton

Pour obtenir la valeur de l'attribut, vous avez besoin du type auquel s'applique l'attribut. Votre méthode d'extension n'obtient qu'une valeur de chaîne (la valeur de Title). Par conséquent, vous ne pourrez pas obtenir l'instance réelle d'où provient la chaîne. Vous ne pouvez donc pas obtenir le type d'origine auquel la propriété Title appartient. Cela rendra impossible d'obtenir la valeur d'attribut à partir de votre méthode d'extension.

0
NerdFury