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?
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; }
}
}
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;
}
}
}
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.
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");
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.
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.