12
votes

Comment utiliser la réflexion pour simplifier les constructeurs et les comparaisons?

Je déteste avoir un tas de méthodes "gauche / droite". Chaque fois qu'une propriété est ajoutée ou enlevée, je dois réparer chaque méthode. Et le code lui-même vient de regarder ... mal. XXX PRE>

Vraiment c'est juste une boucle déroulante qui itère à travers les propriétés, les copiant entre les objets. Alors pourquoi ne pas être honnête à propos de ce fait? Réflexion à la rescousse! P>

public override string ToString()
{
    var toJoin = new List<string>();
    foreach (var property in tostringFields)
    {
        object value = property.GetValue(this, null);
        if (value != null)
            toJoin.Add(value.ToString());
    }
    return string.Join(" ", toJoin.ToArray());
}
private static readonly PropertyInfo[] tostringFields =
{
    typeof(IFoo).GetProperty("Bar"),
    typeof(IFoo).GetProperty("Baz"),
    typeof(IFoo).GetProperty("Lur"),
    typeof(IFoo).GetProperty("Qux"),
    typeof(IFoo).GetProperty("Xyzzy"),
};


8 commentaires

Je voudrais ajouter une étiquette de langue pour obtenir plus de téléspectateurs


Gardez à l'esprit, la réflexion vient toujours avec une performance frappée dans C #. À quelle fréquence allez-vous manipuler des foos comme ça? Une solution hybride pourrait être d'utiliser la réflexion.EMIT et cache le code résultant - Startup plus lent, exécution rapide.


C # 4.0 arrive avec Compilateur intégré, qui peut être utile pour générer du code et l'utiliser, pour l'instant, nous avons également un problème similaire, mais nous avons conçu la solution en utilisant des structures de classe XML et nous utilisons notre propre méthode de génération de code pour créer des classes qui généreront des classes qui généreront Beaucoup d'autres méthodes dépendent des noms des memards, c'est fondamentalement un orml mais tout fait automatique.


FWIW, je viens de faire une comparaison rapide entre les constructeurs de copies à droite / gauche et à la réflexion. La réflexion a pris plus de 3 300 fois plus longtemps que le code droit / gauche. Merci pour l'idée, Akash. Cela peut être utile de faire quelque chose comme ça si cela devient trop lourd.


C'est un peu tard pour le match, mais je pensais que vous voudrez peut-être savoir sur MOWORAPPER. Surpris que personne ne l'a mentionné. Vous pouvez l'utiliser pour convertir un type à un autre par convention. S'ils ne correspondent pas exactement (ne sont pas le même type), vous pouvez configurer des projections faciles à travers son API fluide. Plutôt sympa.


J'ai ajouté une réponse avec un échantillon à l'aide de Machapper.


C # n'a-t-il pas de méthode clone ()?


Seulement s'il s'agit d'une iclonable et que vous devez écrire la mise en œuvre vous-même, ce qui vous gagne très peu sur un constructeur de copie dans de nombreux cas.


4 Réponses :


5
votes

L'utilisation de la réflexion dans et en soi n'est pas mauvaise, mais vous allez prendre une performance surtout si vous le faites de manière récursive.

Je ne suis pas un fan des constructeurs de copies codés durs, car les développeurs oublient de les mettre à jour quand ils Ajoutez de nouvelles propriétés à une classe. P>

Il existe d'autres moyens d'accomplir ce que vous voulez, y compris Marc Gravells hypercripteur de propriété ou si vous souhaitez apprendre certains IL et OPCODES, vous pouvez utiliser System.Reflection.emit ou même Cecil de Mono . P>

Voici un exemple d'utilisation de l'hypercripteur de propriété que vous pouvez éventuellement adapter à vos besoins: P>

public Foo(IFoo other) {
    foreach (var property in MyCacheProvider.GetProperties<IFoo>())
        property.SetValue(this, property.GetValue(other, null), null);
}


3 commentaires

Type.getProperties fait déjà sa propre mise en cache (heure du premier appel et des appels ultérieurs). Le succès de la performance réelle est l'appel de la propriété.setvalue, que vous pouvez atténuer avec la génération de MSIL, etc.


@Rob Fonseca-Ensor merci, je ne le savais pas.


Hmm ... Si la performance Hit est dans la définition des valeurs, ce n'est probablement pas la voie à suivre. Essayer de faire face à tout ce genre de basse niveau serait certainement trop exclu. Merci pour l'entrée.



2
votes

IMHO, la réflexion est une caractéristique très puissante de C #, mais qui est très susceptible de donner lieu à un code gonflé et qui ajoute beaucoup à la courbe d'apprentissage du code et réduit la maintenabilité. Vous serez plus susceptibles de commettre des erreurs (une fois que le refactoring de base peut entraîner des erreurs), et plus peur de changer le nom de n'importe quel bien (si vous trouvez un meilleur nom) ou des trucs comme ça.

Personnellement, j'ai personnellement un code avec un problème similaire et j'avais la même idée d'ajouter des attributs pour maintenir la commande et etc. Mais mon équipe (y compris moi) pensait qu'il était préférable de perdre du temps à la modification du design nécessaire . Peut-être que ce problème est causé par un mauvais design (Eh bien, c'était dans mon cas, mais je ne peux pas dire la même chose à propos de la tienne).


3 commentaires

Je ne sais pas comment il serait plus sujet à une erreur lors de la modification des propriétés, car tout le point est d'éliminer la nécessité de faire référence aux noms de propriété directement dans le code. À l'heure actuelle, je construis juste un modèle de domaine qui sera éventuellement mappé dans une base de données, d'ajouter / supprimer des propriétés / renommer sera pratiquement sans arrêt pendant le développement (surtout que j'essaie d'essayer TDD pour la première fois)


Vous avez "typeof (ifoo) .geproperty (" bar ")" dans votre code, qui fait référence à des noms de propriété directement


D'où les deux derniers paragraphes de ma question;)



3
votes

Je sais qu'il y a déjà une réponse à cela, mais je voulais souligner qu'il existe une bibliothèque qui combine quelques-unes des stratégies d'atténuation pour l'impact sur la performance que quelques personnes ont discuté.

La bibliothèque est appelée Automapper et il mappe d'un objet à un autre et le fait en créant de manière dynamique un ensemble IL à la volée. Cela garantit que, à part une première fois, vous obtenez des performances supérieures et votre code serait beaucoup plus simple: xxx

Cela a tendance à fonctionner bien et que le bonus supplémentaire de ne pas être inventé ici, que je suis fan de.

J'ai fait des tests de performance et après le premier coup de 20 ms (toujours assez rapide), il était à peu près aussi proche de 0 que vous pouvez obtenir. Assez impressionnant.

J'espère que cela aide quelqu'un.


3 commentaires

Ou vous pouvez utiliser Emitmapper qui est encore plus rapide.


Je ne pense pas que Emitmapper était disponible en 09 lorsque j'ai soumis ceci :)


Eh bien ... il n'est jamais en retard pour améliorer votre réponse :)



2
votes

Le problème de base est que vous essayez d'utiliser une langue typée statiquement typée de manière dynamique.

Il n'y a pas vraiment besoin de quelque chose de fantaisie. Si vous souhaitez être capable d'itérer les propriétés, vous pouvez utiliser une carte <> comme étant le magasin de support pour toutes les propriétés de votre classe.

Par coïncidence C'est exactement la manière dont l'assistant de projet VS implémente les paramètres d'application pour toi. (Voir System.Configuration.ApplicationsSettingsBasebase) C'est aussi très «Lua-like» xxx


2 commentaires

Agréable! Stratégie très intéressante. Cela vous permettrait de boucler sur le magasin de support et de tout copier dans une boucle très simple. Très inventif.


Merci pour le conseil. Je vais probablement finir par l'utiliser à un moment donné, mais ce n'est pas tout à fait le bon ajustement de ma tâche actuelle.