2016-12-09 7 views
2

Je suis en train de refactoriser une fonction qui accepte un délégué optionnel à travers le constructeur. Le délégué est exécuté lorsque des événements sont déclenchés dans la classe. Si le délégué n'est pas passé alors une fonction locale par défaut est utilisé à la place:Quelle est la meilleure façon de gérer les délégués optionnels dans un constructeur C#?

public class Foo 
{ 
    int _memberVariable; 
    readonly Action _onEventOne; 
    readonly Action _onEventTwo; 

    public Foo(Action onEventOne, Action onEventTwo = null) 
    { 
     _memberVariable = 0; 

     _onEventOne = onEventOne; 
     _onEventTwo = onEventTwo ?? DefaultEventTwo; 

     _onEventOne(); 
    } 

    private void DefaultEventTwo() 
    { 
     ++_memberVariable; 
    } 
} 

Je cherche à supprimer la valeur par défaut (ce qui est une interface publique surcharge donc serait préférable), ce qui est en production code donc je ne veux pas changer l'interface sauf si je dois.

Dans un monde idéal, je voudrais utiliser l'enchaînement constructeur:

public Foo(Action onEventOne) : this(onEventOne, DefaultEventTwo) 
{ 
    //CS0120 An object reference is required for the non-static field, method, or property 'Foo.DefaultEventTwo() 
} 

(je comprends pourquoi cela ne fonctionne pas, il suffit de donner un exemple du genre de solution que je voudrais utiliser si cela était pas un constructeur). Comme les délégués sont en lecture seule, je ne peux pas les définir dans une fonction de type initialize partagée.

Existe-t-il un meilleur moyen de gérer un cas comme de simplement passer en null et de l'attraper dans le constructeur principal? Il ne semble pas très élégant, et j'aimerais pouvoir attraper une Action nulle comme une exception idéalement (car si un appelant externe utilisait null au lieu d'utiliser le constructeur surchargé). Je pourrais enlever le readonly des délégués mais encore une fois il ne se sent pas comme une bonne solution car ils sont vraiment en lecture seule.

Toutes les pensées seraient appréciées.

+1

publique 'Foo (Action onEventOne): ce (onEventOne, null)' serait un chemin ou 'public Foo (Action onEventOne): ceci (onEventOne, nouvelle Action (Console.Beep))' ou vous avez une fonction comme: 'public static void NoEvent() { // NeNo() }' et le gérer comme: '..., nouvelle action (NoEvent) ' – TripleEEE

+1

Et puis vous vérifiez simplement' null' et si elle passe, appelez 'DefaultEventTwo' au lieu du délégué (non) fourni. – HimBromBeere

Répondre

1

La seule façon que je pouvais obtenir ce travail était de le faire fonctionner moche (à mon avis).

Vous devez fournir une méthode statique, mais cette méthode statique peut utiliser une référence à ceci pour sortir la méthode réelle.

Voici ce que j'ai trouvé.

public Foo(Action onEventOne) : this(onEventOne, self => self.DefaultEventTwo) 
{ 
    //CS0120 An object reference is required for the non-static field, method, or property 'Foo.DefaultEventTwo() 
} 

public Foo(Action onEventOne, Action onEventTwo = null) : this(onEventOne, self => onEventTwo) 
{ } 

// private constructor, just for the sake of getting it working 
private Foo(Action onEventOne, Func<Foo, Action> onEventTwo = null) 
{ 
    _memberVariable = 0; 

    _onEventOne = onEventOne; 
    _onEventTwo = onEventTwo(this); // <-- 

    _onEventOne(); 
} 

self => self.DefaultEventTwo est la fonction statique pour obtenir l'action. Cette fonction est utilisée dans l'appel à onEventTwo(this) pour obtenir l'événement par défaut de l'instance this.

+0

Je pense qu'il n'y aura pas de solution vraiment sympa, je suis surtout intéressé de voir quelles idées les gens ont eues et c'est certainement le plus intéressant jusqu'ici. Cela fonctionne vraiment bien pour le problème comme indiqué, mais commence à être un peu déroutant si vous ajoutez des événements facultatifs supplémentaires (évidemment pas une marque contre cela ici, mais juste un point à noter si quelqu'un d'autre se produit à l'avenir). –

+0

Oui, il n'est tout simplement pas possible de faire ce que vous voulez sans le rendre statique ou le déplacer dans le constructeur comme dans l'autre réponse. –

1

Ai-je raté quelque chose? Tout ce que vous avez à faire est de supprimer la valeur par défaut et de créer un nouveau constructeur qui n'a qu'un seul argument. Maintenant, dans le constructeur le plus détaillé (celui d'origine), vérifiez si la valeur fournie est null et si oui, réglez _onEventTwo sur DefaultEventTwo.

Pour éviter que quiconque utilise le constructeur réduit il suffit de le faire internal.

EDIT: Concernant la gestion des exceptions. Qu'en est-il en utilisant un constructeur interne comme « principal » que tous les -one autres appellent un indiquant où l'param appel est venu de:

internal Foo(Action onEventOne): this(onEventOne, null, true) { } 
// public API: NULL not allwoed as param 
public Foo(Action onEventOne, Action onEventTwo) : this(onEventOne, onEventTwo, false) { } 
internal Foo(Action onEventOne, Action onEventTwo, bool internalUse) 
{ 
    _memberVariable = 0; 

    _onEventOne = onEventOne; 
    if(onEventTwo == null) 
    { 
     if(!internalUse) throw new ArgumentNullException("onEventTwo"); 
     else this._onEventTwo = DefaultEventTwo; 
    } 
    _onEventOne(); 
} 
+0

Je pense que votre deuxième solution correspond beaucoup mieux à la question des deux (j'ai peut-être formulé la question de façon confuse - votre première solution était ce que j'ai fait en premier mais je cherche des alternatives). Ajouter un argument supplémentaire est une idée intéressante que je n'avais même pas envisagée (semble si évident maintenant), merci! –