J'ai la classe suivante: J'ai recherché un moment et il apparaît qu'un argument passé avec le mot clé refanche ne peut pas être stocké. Essayer d'ajouter l'argument source à une liste ou de l'attribuer à une variable de champ ne lui permet pas de conserver une référence à la référence initiale du déléguée; Donc, mes questions sont: p> merci. p> EDIT: strong> Il apparaît que sans utiliser d'emballage ou de réflexion, il n'y a pas de solution au problème donné. Mon intention était de rendre la classe autant portée possible que possible, sans avoir à envelopper des délégués dans des classes d'assistance. Merci à tous pour les contributions. P> p>
6 Réponses :
EDIT STRY>: OK, c'était une mauvaise idée, alors retour aux bases: Je recommande de créer une classe d'emballage sur une action: p> private ActionWrapper localSource;
public void Subscribe(ActionWrapper source)
{
source.Action += Broadcast;
localSource = source;
}
public void Dispose()
{
localSource.Action -= Broadcast;
}
@Mike Christensen: Oui, je suis probablement mal compris. Édité maintenant.
Cela ne fonctionnera pas. Une fois stocké dans un champ ou dans une liste, la référence au délégué réel n'est pas stockée. Je viens de le tester pour être sûr et, en fait, cela ne se désabonne pas avec succès.
Oui, je pense que l'affiche disait que cela n'a pas fonctionné (les arguments passés par REF ne peuvent pas être stockés?) - Peut-être que l'affiche peut peut-être clarifier ce qu'ils veulent dire.
Y a-t-il une raison pour laquelle vous devez passer par Réf?
Si le délégué n'est pas adopté par REF, la méthode souscrite ne sera pas ajoutée à la liste d'invocation du délégué donné. Je suppose que cela crée une nouvelle instance du délégué qui n'est pas corrigée dans le gestionnaire d'origine.
@ user1098567: J'ai fait une édition que je pense est la solution.
Merci. Cela travaillerait certainement gentiment, mais je me demandais, comme mentionné dans le quatrième point de la question (que j'ai édité dans un instant plus tard, je m'excuse si c'était invisible), s'il était possible de le faire sans emballer l'événement / le délégué dans une classe d'assistance.
Je me demande si vous pouvez faire quelque chose de complètement funky, par exemple, écrivez une conversion implicite entre ActionWrapper code> et
délégué code> ..
@ user1098567: J'ai bien peur que sans wrapper ce ne soit impossible. Si vous n'utilisez pas Ref, les objets ne sont passés que par la valeur, vous ne pouvez donc pas les modifier. Si vous utilisez Réf, ils ne peuvent pas être stockés, comme vous l'avez dit. Donc, le seul moyen de faire une "Réfertitude qui peut aussi être stockée" est d'utiliser une enveloppe.
@ user1098567, rappelez-vous que l'utilisation de la solution ci-dessus rendra une référence à une référence à ActionWrapper, qui ne sera pas collectée et peut générer des fuites de mémoire.
public class Terminal : IDisposable { List<IListener> _listeners; List<Action<string>> _sources; public Terminal(IEnumerable<IListener> listeners) { _listeners = new List<IListener>(listeners); _sources = new List<Action<string>>(); } public void Subscribe(ref Action<string> source) { _sources.Add( source ); source += Broadcast; } void Broadcast(string message) { foreach (var listener in _listeners) listener.Listen(message); } public void Dispose() { foreach ( var s in _sources ) s -= Broadcast; } }
edit: strong> Yep, mes mauvais délégués sont des types immuables, l'ajout d'une méthode à une liste d'invocation créera en réalité une nouvelle instance de délégué. P> qui mène à une réponse pas em> à votre question. Pour vous désabonner au délégué, vous devez supprimer votre méthode code> code> de la liste d'invocation du délégué. Cela signifie créer un nouveau délégué et l'attribuer au champ d'origine ou à la variable. Mais vous ne pouvez pas accéder à l'original une fois que vous êtes sorti de Je suggère de déclarer une interface avec l'événement à votre usage. Ce sera une approche assez flexible. P> réponse originale strong> p> est une raison particulière pour laquelle vous passez Sinon, le délégué est un type de référence, vous pouvez donc vous y souscrire sans le transmettre comme abonnez-vous code> méthode. De plus, il peut y avoir d'autres copies de ce champ d'origine / variable ayant votre méthode sur la liste d'invocation. Et il n'ya aucun moyen de savoir à tous et de changer les valeurs. P>
source code> délégué comme
ref code>? Vous avez besoin de cela si vous voulez que vous souhaitiez retourner un délégué différent de la méthode. P>
ref Code> ... p> p>
Malheureusement, non, vous pouvez le tester aussi; Si vous ne passez pas l'instance du délégué par REF, vous vous abonnez-vous ne fonctionnera pas.
Je suggérerais que la méthode d'abonnement devait renvoyer une mise en œuvre d'une classe d'abonnement, qui implémente Idisposable. Une implémentation simple serait pour l'abonnement à l'abonnement de tenir une référence à la liste d'abonnement et à une copie du délégué de l'abonnement; La liste d'abonnement elle-même serait une liste Une telle approche serait beaucoup plus propre que la méthode Délégate.combine / Délégate.ReMove utilisé par le modèle Normal .NET, dont la sémantique peut devenir très étrange si une tentative est faite pour s'abonner et désabonner des délégués multi-cibles. p>
Je m'excuse, mais j'ai bien peur de ne pas comprendre: la partie critique pour moi tiendrait une référence au délégué initial afin de se désabonner de celui-ci (modifiant ainsi sa liste d'abonnement d'origine). Comment le système d'abonnement aurait-il une référence valide au délégué donné?
@ user1098567: Le constructeur de l'abonnement à l'abonnement prendrait le délégué comme paramètre. Étant donné que plusieurs abonnements utilisant le même délégué généreraient des objets d'abonnement distincts et que la liste des abonnements constituerait une liste de souscriptions plutôt que des délégués, appeler le décollage de l'objet renvoyé par une deuxième demande d'abonnement à l'aide d'un délégué particulier annulerait la deuxième souscription utilisée. Délégué, même le délégué avait été souscrit plus de fois après la deuxième demande d'abonnement.
C'est assez simple, mais il y a quelques pièges. Si vous stockez une référence aux objets source, comme la plupart des exemples ont proposé jusqu'à présent, Donc, tout ce que vous avez à faire est celui-ci: p> 1) Ajouter une liste de sources à la classe: P> public void Dispose()
{
foreach (var r in _sources)
{
var source = (Action<string>) r.Target;
if (source != null)
{
source -= Broadcast;
source = null;
}
}
_sources.Clear();
}
Bonjour, malheureusement, cela fait une différence si l'action est transmise en tant que réf. Si vous le transmettez comme une référence, il vous abonnera avec succès, mais en utilisant votre méthode, ne vous désabonnera pas. En passant sans mot-clé refer, il ne sera tout simplement pas abonné.
@ user1098567, après avoir lu l'autre réponse et effectuer des tests, j'ai compris le problème plus clairement. L'action n'est pas immuable (sinon, pas même en passant par REF fonctionnerait), mais c'est un apprécipé et en tant que tel, ne peut pas être cloné. C'était vraiment une belle question, m'a fait penser à des choses. Soyez juste prudent lorsque vous vous abonnez et utilisez des références faibles pour être en sécurité contre les fuites de mémoire.
Peut-être, au lieu d'essayer de stocker une référence au délégué, apporter des appels à souscrire utilisez sa référence à l'objet avec le délégué pour créer des actions pour l'abonnement et la désinscription. C'est un paramètre supplémentaire, mais c'est toujours simple.
public event Action<string> CallOccured; public void Program() { Subscribe(a => CallOccured += a, a => CallOccured -= a); CallOccured("Hello"); }
Utiliserait un événement avec un
Ajouter
/
Supprimer code> Le gestionnaire fonctionne mieux pour vous?
Il serait plus facile de mettre en œuvre une classe wrapper puis, car j'ai beaucoup d'événements et écrire des implémentations personnalisées pour eux rendrait le code assez illisible. Mon but est de rendre la classe autant compatible que possible.
Mon exemple n'utilise pas d'emballage, même si cela nécessite un peu plus de travail supplémentaire (créant / passant deux délégués au lieu d'un délégué ref). J'ai remarqué que le framework rx est Quelque chose similiaire < / a>. Cela me rappelait cette question et ma réponse. Vaut probablement la peine de regarder pour votre solution.