Récemment, je suis venu à préférer le mappage des relations 1-1 en utilisant Dictionaries
au lieu des Switch
instructions. Je trouve que c'est un peu plus rapide à écrire et plus facile à traiter mentalement. Malheureusement, lors du mappage vers une nouvelle instance d'un objet, je ne veux pas le définir comme ceci:
var fooDict = new Dictionary<int, IBigObject>()
{
{ 0, new Foo() }, // Creates an instance of Foo
{ 1, new Bar() }, // Creates an instance of Bar
{ 2, new Baz() } // Creates an instance of Baz
}
var quux = fooDict[0]; // quux references Foo
Compte tenu de cette construction, j'ai gaspillé des cycles CPU et de la mémoire en créant 3 objets, en faisant tout ce que leurs constructeurs pourraient contenir, et je n'ai fini par utiliser l'un d'eux. Je crois également que le mappage d'autres objets fooDict[0]
dans ce cas les amènera à référencer la même chose, plutôt que de créer une nouvelle instance de Foo
comme prévu. Une solution serait d'utiliser un lambda à la place:
var fooDict = new Dictionary<int, Func<IBigObject>>()
{
{ 0, () => new Foo() }, // Returns a new instance of Foo when invoked
{ 1, () => new Bar() }, // Ditto Bar
{ 2, () => new Baz() } // Ditto Baz
}
var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'
Est-ce que cela arrive à un point où c'est trop déroutant? C'est facile de manquer ça ()
à la fin. Ou le mappage à une fonction / expression est-il une pratique assez courante? L'alternative serait d'utiliser un interrupteur:
IBigObject quux;
switch(someInt)
{
case 0: quux = new Foo(); break;
case 1: quux = new Bar(); break;
case 2: quux = new Baz(); break;
}
Quelle invocation est la plus acceptable?
- Dictionnaire, pour des recherches plus rapides et moins de mots-clés (casse et pause)
- Switch: plus communément trouvé dans le code, ne nécessite pas l'utilisation d'un objet Func <> pour l'indirection.
la source
fooDict[0] is fooDict[0]
). avec le lambda et le commutateur ce n'est pas le casRéponses:
C'est une interprétation intéressante du modèle d'usine . J'aime la combinaison du dictionnaire et de l'expression lambda; cela m'a fait regarder ce conteneur d'une nouvelle façon.
J'ignore la préoccupation dans votre question concernant les cycles de processeur, comme vous l'avez mentionné dans les commentaires selon lesquels l'approche non lambda ne fournit pas ce dont vous avez besoin.
Je pense que l'une ou l'autre approche (commutateur vs dictionnaire + lambda) ira bien. La seule limitation est qu'en utilisant le dictionnaire, vous limitez les types d'entrées que vous pourriez recevoir afin de générer la classe retournée.
L'utilisation d'une instruction switch vous offrirait plus de flexibilité sur les paramètres d'entrée. Cependant, si cela devait être un problème, vous pourriez envelopper le dictionnaire à l'intérieur d'une méthode et avoir le même résultat final.
Si c'est nouveau pour votre équipe, commentez le code et expliquez ce qui se passe. Appelez pour une révision du code d'équipe, expliquez-leur ce qui a été fait et informez-le. A part ça, ça a l'air bien.
la source
case 0: quux = new Foo(); break;
devientcase 0: return new Foo();
qui est franchement aussi facile à écrire et beaucoup plus facile à lire que{ 0, () => new Foo() }
C # 4.0 vous donne la
Lazy<T>
classe, qui est similaire à votre propre deuxième solution, mais crie plus explicitement "l'initialisation paresseuse".la source
Stylistiquement, je pense que la lisibilité est égale entre eux. Il est plus facile de faire une injection de dépendance avec le
Dictionary
.N'oubliez pas que vous devez vérifier si la clé existe lors de l'utilisation de la
Dictionary
, et devez fournir une solution de repli dans le cas contraire.Je préférerais la
switch
déclaration pour les chemins de code statiques et laDictionary
pour les chemins de code dynamiques (où vous pourriez ajouter ou supprimer des entrées). Le compilateur peut être en mesure d'effectuer certaines optimisations statiques avec leswitch
qu'il ne peut pas avec leDictionary
.Fait intéressant, ce
Dictionary
modèle est ce que les gens font parfois en Python, car Python n'a pas l'switch
instruction. Sinon, ils utilisent des chaînes if-else.la source
En général, je ne préférerais ni l'un ni l'autre.
Tout ce qui consomme cela devrait fonctionner avec a
Func<int, IBigObject>
. Ensuite, la source de votre mappage peut être un dictionnaire ou une méthode qui a une instruction switch ou un appel de service Web ou une recherche de fichier ... peu importe.En ce qui concerne l'implémentation, je préférerais le dictionnaire car il est plus facilement refactorisé de «dictionnaire de code dur, clé de recherche, résultat de retour» à «charger le dictionnaire à partir du fichier, clé de recherche, résultat de retour».
la source