2009-04-21 4 views
1

J'essaie d'obtenir toutes les propriétés d'un type, mais l'utilisation de TypeDescriptor.GetProperties (thisType) ne me fournira que des propriétés, qui ont à la fois setter et un getter. J'ai des propriétés en écriture seule. Existe-t-il un moyen de récupérer un PropertyDescriptorCollection, y compris ceux-ci?TypeDescriptor.GetProperties (thisType) pour renvoyer des propriétés, qui sont en écriture seule

/Asger

+0

Re vos commentaires; Je vais poster une mise à jour avec un "truc" pour le scénario où vous ne connaissez pas les types ... –

+0

En fait, je viens de relire vos commentaires; est la propriété en écriture seule sur l'interface? Si c'est le cas, vous pouvez utiliser le code original (voir les modifications) et utiliser simplement l'interface comme type reflété. –

+0

Oui, la propriété est sur l'interface. J'ai essayé cela avec votre exemple original (avant les modifications, si c'est ce que vous voulez dire?), Mais j'ai eu une exception de ne pas pouvoir lier le délégué - ou quelque chose comme ça - je dois réessayer d'obtenir le message d'exception . – asgerhallas

Répondre

9

Les propriétés en écriture seule sont rares et n'existent pas dans l'espace System.ComponentModel/PropertyDescriptor. PropertyDescriptor s sont conçus pour être lisibles. Je pourrais probablement pirater HyperDescriptor pour caler les propriétés en écriture seule, mais ce serait un hack - et il faudrait vraisemblablement jeter des exceptions pour get, ce qui pourrait avoir un impact sur le code d'appel.

En aparté; Je conseille généralement contre propriétés en écriture seule; l'exemple de livre de texte que les gens trotter est des mots de passe (public string Password {private get;set;}) - Je préfère de beaucoup avoir une méthode void SetPassword(string newPassword) ...

Qu'est-ce que vous voulez réellement faire? Il existe une gamme d'options ici, très réalisable:

  • utilisation seule réflexion (lent, peut-être pas une option)
  • utilisation Delegate.CreateDelegate (très facile)
  • utilisation Expression.Compile (un peu plus difficile, mais pas beaucoup)
  • utilisation Reflection.Emit (assez difficile)
  • propriétés shim en écriture seule dans PropertyDescriptor (assez difficile)

Si vous me faites savoir ce que vous voulez réellement faire (plutôt que la façon dont vous essayez actuellement de le faire), je pourrais peut-être vous aider davantage.

À titre d'exemple en utilisant Delegate.CreateDelegate (note que vous voulez cacher quelque part le délégué et réutilisez-le beaucoup de fois):

édités pour montrer comment le faire si vous ne connaissez pas les types spécifiques lors de l'exécution

using System; 
using System.Reflection; 

class Foo 
{ 
    public string Bar { private get; set; } 
    public override string ToString() 
    { 
     return Bar; // to prove working 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     ISetter setter = Setter.Create(typeof(Foo), "Bar"); 
     Foo foo = new Foo(); 
     setter.SetValue(foo, "abc"); 
     string s = foo.ToString(); // prove working 
    } 
} 
public interface ISetter { 
    void SetValue(object target, object value); 
} 
public static class Setter 
{ 
    public static ISetter Create(Type type, string propertyName) 
    { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 
     return Create(type.GetProperty(propertyName)); 
    } 
    public static ISetter Create(PropertyInfo property) 
    { 
     if(property == null) throw new ArgumentNullException("property"); 
     if (!property.CanWrite) throw new InvalidOperationException("Property cannot be written"); 
     Type type = typeof(TypedSetter<,>).MakeGenericType(
       property.ReflectedType, property.PropertyType); 
     return (ISetter) Activator.CreateInstance(
      type, property.GetSetMethod()); 
    } 
} 

public class TypedSetter<TTarget, TValue> : ISetter { 
    private readonly Action<TTarget, TValue> setter; 
    public TypedSetter(MethodInfo method) { 
     setter = (Action<TTarget, TValue>)Delegate.CreateDelegate(
      typeof(Action<TTarget, TValue>), method); 
    } 
    void ISetter.SetValue(object target, object value) { 
     setter((TTarget)target, (TValue)value); 
    } 
    public void SetValue(TTarget target, TValue value) { 
     setter(target, value); 
    } 
} 

Ou bien en utilisant l'API Expression (.NET 3.5):

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

class Foo 
{ 
    public string Bar { private get; set; } 
    public override string ToString() 
    { 
     return Bar; // to prove working 
    } 
} 
static class Program 
{ 
    static void Main() 
    { 
     Action<object,object> setter = Setter.Create(typeof(Foo), "Bar"); 
     Foo foo = new Foo(); 
     setter(foo, "abc"); 
     string s = foo.ToString(); 
    } 
} 

public static class Setter 
{ 
    public static Action<object,object> Create(Type type, string propertyName) 
    { 
     if (type == null) throw new ArgumentNullException("type"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 
     return Create(type.GetProperty(propertyName)); 
    } 
    public static Action<object,object> Create(PropertyInfo property) 
    { 
     if(property == null) throw new ArgumentNullException("property"); 
     if (!property.CanWrite) throw new InvalidOperationException("Property cannot be written"); 

     var objParam = Expression.Parameter(typeof(object), "obj"); 
     var valueParam = Expression.Parameter(typeof(object), "value"); 
     var body = Expression.Call(
      Expression.Convert(objParam, property.ReflectedType), 
      property.GetSetMethod(), 
      Expression.Convert(valueParam, property.PropertyType)); 
     return Expression.Lambda<Action<object, object>>(
      body, objParam, valueParam).Compile(); 
    } 
} 
+0

Salut Marc, merci beaucoup pour votre réponse rapide! Les setters fonctionnent comme un proxy pour configurer certains éléments d'affichage dans une application webforms. Un présentateur, via une interface d'affichage, définit la configuration requise sur la vue: il utilise la réflexion sur l'interface et l'implémentation de la vue pour finir par utiliser SetValue pour définir la configuration sur la vue. Au fur et à mesure que nos systèmes se développent et qu'il y a beaucoup de vues (imbriquées les unes dans les autres), la partie réflexion est vraiment lente. – asgerhallas

+0

Je pense que votre solution de délégué semble être la bonne approche pour moi - j'ai la logique pour le SetValue caché dans une classe générale. Je vais l'essayer immédiatement. Y a-t-il d'autres documentations/"Getting started writeups" sur toutes les autres possibilités que vous recommanderiez? – asgerhallas

+0

Je viens de regarder de plus près. Un problème avec la solution déléguée, c'est que je ne connais pas le type d'implémentation de la vue (votre Foo) au moment de la compilation, seulement l'interface que Foo implémente. Au moment de l'exécution, j'ai le type. Ensuite, je devrais utiliser la réflexion pour créer le délégué - je ne sais pas si cela va ruiner la performance alors ... – asgerhallas

2

Utilisez System.Type.GetProperties() à la place, qui renvoie toutes les propriétés. Notez que cela renvoie un PropertyInfo[] au lieu d'un PropertyDescriptorCollection.

+1

FYI: seule la version sans paramètre renvoie toutes les propriétés publiques.Il y a une surcharge qui prend quelques BindingFlags que vous pouvez utiliser pour obtenir toutes les propriétés si vous les voulez. –

+0

Merci pour l'aide. Mais j'ai besoin du PropertyDescriptor et non du PropertyInfo car j'utilise Marc Gravell HyperTypeDescriptor parce que og problèmes de perfomance. Donc, utiliser Type.GetProperties n'est malheureusement pas une solution. – asgerhallas

+0

Ai-je entendu mon nom être invoqué? Je vais ajouter une réponse ... –

Questions connexes