J'écris un système qui sous-tend les applications de programmeurs et qui a besoin de détecter leur accès à certaines données. Je peux le faire la plupart du temps avec des propriétés, comme ceci:Une manière "élégante" d'identifier un champ?
public class NiceClass {
public int x { get; set; }
}
Ensuite, je vais et tweak les get
et set
accesseurs afin qu'ils gèrent les accès de manière appropriée. Cependant, cela nécessite que les utilisateurs (programmeurs d'applications) définissent toutes leurs données en tant que propriétés.
Si les utilisateurs veulent utiliser des classes préexistantes qui ont des champs «normaux» (par opposition aux propriétés), je ne peux pas détecter ces accès. Exemple:
public class NotSoNiceClass {
public int y;
}
Je ne peux pas détecter les accès à y
. Cependant, je veux autoriser l'utilisation de classes préexistantes. En tant que compromis les utilisateurs sont chargés de me notifier chaque fois qu'un accès à ce genre de données se produit. Par exemple:
NotSoNiceClass notSoNice;
...
Write(notSoNice.y, 0); // (as opposed to notSoNice.y = 0;)
Quelque chose comme ça. Croyez-moi, je l'ai étudié très minutieusement et même en analysant directement le bytecode pour détecter les accès n'est pas fiable en raison des indirections possibles, etc. J'ai vraiment besoin des utilisateurs pour me prévenir.
Et maintenant ma question: pourriez-vous recommander une façon «élégante» d'effectuer ces notifications? (Oui, je sais que toute cette situation n'est pas "élégante" pour commencer, j'essaie de ne pas l'aggraver;)). Comment le feriez-vous?
Ceci est un problème pour moi parce qu'en fait la situation est comme ça: je la classe suivante:
public class SemiNiceClass {
public NotSoNiceClass notSoNice { get; set; }
public int z { get; set; }
}
Si l'utilisateur veut faire:
SemiNiceClass semiNice;
...
semiNice.notSoNice.y = 0;
Ils doivent plutôt faire quelque chose comme ceci:
semiNice.Write("notSoNice").y = 0;
Où Write
retournera un clone de notSoNice
, qui est ce que je voulais que l'accesseur set
fasse de toute façon. Cependant, l'utilisation d'une chaîne est plutôt moche: si plus tard, ils refactorisent le champ, ils devront passer par-dessus leurs accès Write("notSoNice")
et changer la chaîne.
Comment pouvons-nous identifier le champ? Je ne peux penser qu'à des cordes, des ints et des enums (c'est-à-dire, ints à nouveau). Mais:
- Nous avons déjà discuté du problème avec les chaînes.
- Ints sont une douleur. Ils sont encore pires car l'utilisateur doit se rappeler quel int correspond à quel champ. Le refactoring est également difficile.
- énumérations (tels que
NOT_SO_NICE
etZ
, à savoir, les champs deSemiNiceClass
) facilité refactoring, mais ils exigent que l'utilisateur d'écrire un ENUM par classe (SemiNiceClass
, etc.), avec une valeur par champ de la classe. C'est ennuyant. Je ne veux pas qu'ils me détestent;)
Alors pourquoi, je vous entends demander, pouvons-nous ne pas le faire (ci-dessous)?
semiNice.Write(semiNice.notSoNice).y = 0;
Parce que je dois savoir ce champ est accessible, et semiNice.notSoNice
ne permettent pas d'identifier un champ. C'est la valeur du champ, pas le champ lui-même.
Soupir. Je sais que c'est moche. Croyez-moi;)
J'apprécierai grandement les suggestions.
Merci d'avance!
(Aussi, je ne pouvais pas trouver de bonnes balises pour cette question S'il vous plaît laissez-moi savoir si vous avez de meilleures idées, et je vais les modifier.)
EDIT # 1: Suggestions de Hightechrider: Expressions.
Je ne sais pas si Write(x =>semiNice.y, 0)
était censé être basé sur les classes que j'ai écrites pour ma question (SemiNiceClass
, etc) ou si c'est juste un exemple, mais si c'est le premier, il ne correspond pas à la structure: il y a pas de champ y
dans SemiNiceClass
. Vouliez-vous dire Write(x =>semiNice.notSoNice.y, 0)
?
Je ne sais pas comment vous dire pour moi d'utiliser cette ... Je vais poster le code que j'ai écrit:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test.MagicStrings {
public class MagicStringsTest {
public static void Main(string[] args) {
SemiNiceClass semiNice = new SemiNiceClass();
// The user wants to do: semiNice.notSoNice.y = 50;
semiNice.Write(x => semiNice.notSoNice.y, 50);
}
}
public class SemiNiceClass {
public NotSoNiceClass notSoNice { get; set; }
public int z { get; set; }
public SemiNiceClass() {
notSoNice = new NotSoNiceClass();
}
public void Write(Func<object, object> accessor, object value) {
// Here I'd like to know what field (y, in our example) the user wants to
// write to.
}
}
public class NotSoNiceClass {
public int y;
}
}
Comment puis-je obtenir cette information en Write
? Je ne trouve aucun moyen d'extraire cette information sur le Func<,>
. En outre, pourquoi écrire semiNice.Write(x => semiNice.notSoNice.y, 50);
au lieu de semiNice.Write(() => semiNice.notSoNice.y, 50);
, puisque nous n'utilisons pas x
pour quelque chose?
Merci.
EDIT # 2: suggestion de Hans Passant: remplacer les champs avec des propriétés.
C'est ce que j'avais l'intention de faire à l'origine, mais la recompilation n'est pas une option.
EDIT # 3: la suggestion de Ben Hoffstein: procurations dynamiques; LinFu.
J'ai déjà regardé dans ce long et dur, et pour des raisons relativement complexes je ne peux pas l'utiliser. Ce serait trop long à expliquer, mais rassurez-vous: si je pouvais l'utiliser, je le ferais. C'est beaucoup, beaucoup plus propre que ma solution actuelle.
J'ai mis à jour ma question. Voir EDIT # 1. Merci :) – Alix
Et j'ai mis à jour la réponse pour vous. –
Ah, parfait. +1, et je marque cette réponse et Akash comme les réponses. Merci beaucoup :) – Alix