2010-09-14 18 views
4

Disons que j'ai une classe qui ressemble à ceci:Appeler une propriété ou une méthode utilisant un nom d'attribut

public class CallByAttribute 
{ 
    [CodeName("Foo")] 
    public string MyProperty { get; set; } 

    [CodeName("Bar")] 
    public string MyMethod(int someParameter) 
    { 
     return myDictionary[someParameter]; 
    } 
} 

Comment puis-je appeler ces deux propriétés ou méthodes, en utilisant CodeName au lieu du nom de la propriété ou de la méthode?

Répondre

5

Méthode 1:

public static TOutput GetPropertyByCodeName<TOutput>(this object obj, string codeName) 
{ 
    var property = obj.GetType() 
         .GetProperties() 
         .Where(p => p.IsDefined(typeof(CodeNameAttribute), false)) 
         .Single(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false).First())).Name == codeName); 

    return (TOutput)property.GetValue(obj, null); 
} 

Note: Ceci jeter si aucune propriété existe avec le spécifié codeName ou si plusieurs propriétés partagent le même codeName.

Utilisation:

CallByAttribute obj= ... 
string myProperty = obj.GetPropertyByCodeName<string>("Foo"); 

Méthode 2:

Si vous êtes sur C# 4, vous pouvez écrire votre propre System.Dynamic.DynamicObject qui peut les appels dynamiques de route vers le membre droit.

Ceci permettra une syntaxe plus propre. Par exemple, vous devriez être en mesure d'accomplir quelque chose qui permet:

CallByAttribute obj= ... 
dynamic objectByCodeName = new ObjectByCodeName(obj); 
objectByCodeName.Foo = "8"; 
objectByCodeName.Bar(); 
+0

Il semble que vous ayez besoin d'une classe 'Attribute' pour' CodeName'; est-ce vrai, ou pouvez-vous juste réfléchir sur les propriétés pour obtenir le nom de code pour chacun? –

+2

Robert: Vous avez probablement déjà une classe 'CodeNameAttribute', sinon vous ne pourriez pas faire' [CodeName ("foo")] 'parce que cela provoque une instanciation d'un objet' CodeNameAttribute'. – Gabe

+1

Si vous utilisez beaucoup ces noms de code (des milliers ou des millions de fois), vous devez mettre en cache les résultats de la recherche de propriété en tant que délégués que vous pouvez rechercher dans un dictionnaire. – Gabe

3

Voici un code entièrement fonctionnel, y compris les arguments d'appel en option:

private static string Call(object callByAttribute, string name, object[] args) 
    { 
     PropertyInfo prop = callByAttribute.GetType().GetProperties() 
      .Where(p => p.IsDefined(typeof(CodeNameAttribute), false)) 
      .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name); 
     if (prop != null) 
      return (string)callByAttribute.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty, null, callByAttribute, null); 


     MethodInfo method = callByAttribute.GetType().GetMethods() 
      .Where(p => p.IsDefined(typeof(CodeNameAttribute), false)) 
      .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name); 
     if (method != null) 
      return (string)callByAttribute.GetType().InvokeMember(method.Name, BindingFlags.InvokeMethod, null, callByAttribute, args); 

     throw new Exception("method/getter not found"); 
    } 
    private static string Call(object callByAttribute, string name) 
    { 
     return Call(callByAttribute, name, null); 
    } 

Ceci peut être utilisé dans un programme complet comme celui-ci:

using System; 
using System.Linq; 
using System.Reflection; 

namespace ConsoleApplication1 
{ 
public class CallByAttribute 
{ 
    [CodeName("Foo")] 
    public string MyProperty { get; set; } 

    [CodeName("Bar")] 
    public string MyMethod(int someParameter) 
    { 
     return "blah" + someParameter; 
    } 
} 

public class CodeNameAttribute : Attribute 
{ 
    private readonly string name; 

    public CodeNameAttribute(string name) 
    { 
     this.name = name; 
    } 

    public string Name 
    { 
     get { return name; } 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     CallByAttribute callByAttribute = new CallByAttribute(); 
     callByAttribute.MyProperty = "hi"; 

     Console.WriteLine(Call(callByAttribute, "Bar", new object[] {1})); 
     Console.WriteLine(Call(callByAttribute, "Foo")); 

    } 
    private static string Call(object callByAttribute, string name) 
    { 
     return Call(callByAttribute, name, null); 
    } 


    private static string Call(object callByAttribute, string name, object[] args) 
    { 
     PropertyInfo prop = callByAttribute.GetType().GetProperties() 
      .Where(p => p.IsDefined(typeof(CodeNameAttribute), false)) 
      .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name); 
     if (prop != null) 
      return (string)callByAttribute.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty, null, callByAttribute, null); 

     MethodInfo method = callByAttribute.GetType().GetMethods() 
      .Where(p => p.IsDefined(typeof(CodeNameAttribute), false)) 
      .SingleOrDefault(p => ((CodeNameAttribute)(p.GetCustomAttributes(typeof(CodeNameAttribute), false)).First()).Name == name); 
     if (method != null) 
      return (string)callByAttribute.GetType().InvokeMember(method.Name, BindingFlags.InvokeMethod, null, callByAttribute, args); 

     throw new Exception("method/getter not found"); 
    } 
} 
}