2009-10-29 5 views
2

Supposons que j'aichangement C# cible action/délégué: appels hiérarchiques

public IList<Entity> Children { get; set; } 

public NotifyChildren(Func<object, bool> action, object data) 
{ 
    foreach (var child in Children) 
     if (action(data)) 
      /// <-- !!!! need action to work on child this time, not on the original target 
      //child.NofifyChildren(action, data); <- this doesn't work because of the above requirement 
      child.NotifyChildren(action.ChangeTargetTo(child), data); // << pseudocode! 
} 

public void SomeChangeOccured() 
{ 
    var changedChild; 
    NotifyChildren(x => x.SomeHandler(), "somedata"); 
} 

Comment puis-je changer la cible de l'action? Je suis OK pour passer un délégué au lieu de l'action mais son .Target est en lecture seule, aussi. Je pense actuellement en train de faire

public NotifyChildren(Expression<Func<Entity, bool>> action, object data) 
{ 
    // so that I can do method.Invoke(newtarget, new object[]{data}); 
    NotifyChildren(((MethodCallExpression)action).Method, data); 
} 

qui est, passer de l'action à l'appel de la méthode réfléchie ... mais il est un peu laid, est-ce pas?

J'ai le sentiment que la solution est très simple et je la connaissais ... juste oublié. Hm, une solution serait d'avoir un délégué statique qui accepte Entity comme premier paramètre, mais je ne voudrais pas aller de cette façon.

+0

Votre exemple de code situé dans la classe nommée entité? –

+0

En fait, il est situé dans la classe Parent mais cela n'a pas d'importance. Dans cet exemple, je l'ai écrit comme s'il se trouvait dans la classe Enfant. – queen3

+0

En fait, je pense que cela rend votre question ambiguë: Entité est un objet hiérarchique (avec une référence de soi) ou non? Votre objectif est-il de notifier de manière récurrente les entités enfants d'un changement survenu sur votre entité racine? Enfin, quel est le point d'entrée du processus? –

Répondre

4

Qu'est-ce que vous êtes spécifiquement demandant n'est pas possible. Un délégué représente une méthode liée statiquement, alors que vous cherchez quelque chose de dynamique. Pour ce faire, vous avez besoin de réflexion.

Cependant, votre code apparaît pour être structuré de la manière correcte pour accomplir ce que vous voulez, mais le code n'a pas l'air d'être appelé correctement.

Vous définir action comme Func<Entity, bool>, mais vous passez data (ce qui est un object) plutôt que child (ce qui est un Entity). Il semble que vous devriez effectivement déclarer comme Func<Entity, object, bool> et le faire comme ceci:

public IList<Entity> Children { get; set; } 

public NotifyChildren(Func<Entity, object, bool> action, object data) 
{ 
    foreach (var child in Children) 
    { 
     if (action(child, data)) 
     { 
      child.NofifyChildren(action, data); 
     } 
    } 
} 

public void SomeChangeOccured() 
{ 
    NotifyChildren((x, data) => x.SomeHandler(data), "somedata"); 
} 

public bool SomeHandler(object data) 
{ 
    return true; // obviously need more robust logic 
} 
+1

En fait, c'est possible avec des délégués non liés, mais votre solution semble meilleure dans ce cas. FWIW, vous pouvez également créer le délégué directement à partir de 'SomeHandler' si cela ne vous dérange pas d'obtenir son' MethodInfo' avec réflexion. –

+0

J'ai corrigé la signature. Ce que vous avez fourni ne fonctionnerait pas, car l'action fonctionnera toujours sur l'entité de premier niveau d'origine - j'ai besoin qu'elle soit invoquée sur l'enfant. Vous appelez l'action (enfant, données) mais c'est la méthode "délégué statique" que je ne veux pas utiliser (en passant _this_ comme première méthode à la fonction statique). – queen3

+0

@queen: Comme je l'ai dit * ce que vous demandez n'est pas possible *, à moins que vous ne descendiez la route de réflexion. L'utilisation de cette signature permet toujours l'approche «bouillonnante» que vous recherchez.Étant donné que la seule déclaration de méthode réelle est faite dans un lambda, je suis confus quant à la raison de votre objection. –

0
foreach (var child in Children) 
    if (action(data)) 
     /// <-- !!!! need action to work on child this time, not on the original target 
     child.NofifyChildren(action, data); 

Pourquoi appelez-vous une action dans cette boucle? Je pense que vous pouvez l'appeler une seule fois avant.

+0

Parce que j'ai besoin de ;-) l'action ici est juste un nom de méthode, disons "HandleNameChange". Et je veux l'appeler pour tous les enfants, et si les enfants décident de le faire, ils veulent l'appeler sur leurs enfants. C'est une sorte de bulle qui passe. – queen3

+0

où vous passez l'instance enfants à l'action dans cet appel? –

+0

ou action peut être modifiée après child.NofifyChildren (action, données); appel? bu où ref mot-clé dans ce cas? –