2017-08-03 7 views
0

J'ai souscrit quelques méthodes à un événement et que vous souhaitez les invoquer par l'ordre spécifique que je leur donne comme ceci:Invocation commande par un attribut spécifique

foreach (var method in LOAD_DEPENDENCIES.GetInvocationList() 
     .OrderBy(x => x.Method.GetCustomAttributes(typeof(InvocationOrderAttribute),false))) 
{ 
    method.DynamicInvoke(localStats, this); 
} 

où (bien sûr) l'événement est LOAD_DEPENDENCIES et la l'attribut est InvocationOrderAttibute. Note que le corps de foreach fonctionne, les paramètres de DynamicInvoke ne sont pas le problème.

Ce attribut ressemble à ceci:

public class InvocationOrderAttribute : Attribute , IComparable 
{ 
    public int order; 
    public InvocationOrderAttribute(int order) 
    { 
     this.order = order; 
    } 

    public int CompareTo(object obj) 
    { 
     return this.order.CompareTo((obj as InvocationOrderAttribute).order); 
    } 
} 

Je mis en œuvre le IComparable en espérant que OrderBy va utiliser pour déterminer l'ordre.

Cela ne fonctionne pas, par le débogage j'ai vérifié que je n'entrerais même jamais dans le corps de cette boucle foreach. ALL les méthodes souscrites possèdent cet attribut.

Question est, qu'est-ce que je fais mal dans la requête LINQ foreach ou dans l'attribut?

EDIT:

il est pas le meilleur, mais fonctionne:

foreach (var method in LOAD_DEPENDENCIES.GetInvocationList() 
     .OrderBy(y => y.Method.GetCustomAttributes(false) 
     .OfType<InvocationOrderAttribute>().FirstOrDefault().order)) 
{ 
    method.DynamicInvoke(localStats, this); 
} 

Répondre

1

.GetCustomAttributes retourne un object[], donc .OrderBy utilisera Comparer<object[]>.Default, qui jette des exceptions à vous comme il veut utiliser une implémentation IComparable sur le type object[], qui n'existe pas. Au lieu de cela, vous devez utiliser .GetCustomAttribute<InvocationOrderAttribute>, qui renvoie l'attribut ou la valeur null si aucun attribut de ce type n'est présent. Comment les méthodes sans attribut se comparent-elles à celles qui en ont un?

Juste écrit un petit exemple qui fonctionne avec aucun gestionnaire d'événements ainsi qu'avec des gestionnaires d'événements ne portant pas l'attribut, où ces derniers précèdent le premier dans l'ordre. L'événement est de type délégué Action dans l'exemple (le vôtre est quelque chose d'autre).

EDIT: C# version 4.0 capable sans CustomAttributeExtensions

L'attribut:

[AttributeUsage(AttributeTargets.Method)] 
public sealed class InvocationOrderAttribute : Attribute 
{ 
    public int Order { get; private set; } 

    public InvocationOrderAttribute(int order) 
    { 
     Order = order; 
    } 
} 

Nouveau: méthode utile pour tous les types d'événements

/// <summary> 
/// Get individual handlers of the invocation list of the specified <paramref name="@event"/>, 
/// ordered by the <see cref="InvocationOrderAttribute"/> of the handler's method. 
/// </summary> 
/// <typeparam name="TDelegate">Delegate type of the <paramref name="@event"/>.</typeparam> 
/// <exception cref="ArgumentException"><typeparamref name="TDelegate"/> is not a delegate type.</exception> 
/// <remarks>Handlers without the attribute come last.</remarks> 
public static IEnumerable<TDelegate> OrderedInvocationList<TDelegate>(TDelegate @event) 
{ 
    if (!typeof(Delegate).IsAssignableFrom(typeof(TDelegate))) 
     throw new ArgumentException(typeof(TDelegate) + " is not a delegate type."); 

    if (@event == null) // empty invocation list 
     return Enumerable.Empty<TDelegate>(); 

    return ((Delegate)(object)@event).GetInvocationList() 
     .Select(handler => 
     { 
      var attribute = (InvocationOrderAttribute)handler.Method.GetCustomAttributes(typeof(InvocationOrderAttribute), false).FirstOrDefault(); 
      return new 
      { 
       Handler = (TDelegate)(object)handler, 
       Order = attribute != null ? attribute.Order : int.MaxValue 
      }; 
     }) 
     .OrderBy(ho => ho.Order) 
     .Select(ho => ho.Handler); 
} 

Utilisation:

public static class Program 
{ 
    private static event Action Event; 

    private static void RaiseEvent() 
    { 
     foreach (var h in MyAwesomeCode.OrderedInvocationList(Event)) 
      h(); 
    } 

    [InvocationOrder(1)] 
    private static void M1() { Console.WriteLine("M1"); } 

    [InvocationOrder(2)] 
    private static void M2() { Console.WriteLine("M2"); } 

    private static void M3() { Console.WriteLine("M3"); } 

    public static void Main() 
    { 
     RaiseEvent(); // works on empty invocation list 

     Event += M3; 
     Event += M2; 
     Event += M1; 
     Event += M3; 
     Event += M2; 
     Event += M1; 

     RaiseEvent(); // works with methods not carrying the attribute 
    } 
} 

Sortie: M1 M2 M1 M2 M3 M3

Les améliorations par rapport à votre code (y compris 2ème solution):

  • Pas NullReferenceException si aucun gestionnaire enregistré.
  • Non NullReferenceException si un gestionnaire n'a aucun attribut.
  • Ne reflétant pas l'attribut lors du tri.
  • Applicable à tous les types de délégués d'événement.
  • Non .DynamicIvoke (laid, pas refactorisation amical, inefficace)
  • (Ne pas appeler "méthode" d'un délégué, en utilisant boîtier standard identifiants.)
+0

Merci pour votre réponse. J'utilise ceci pour Unity et avec la version C# 4, où je n'ai pas 'GetCustomAttribute <>' (peut-être pas à cause de la version mais je n'en ai pas). Quelle solution de contournement fonctionnerait dans ce cas? – agiro

+1

'.GetCustomAttributes(). OfType (). FirstOrDefault()' est équivalent – tinudu

+0

Pas de chance:/ne fait toujours pas ce que je souhaite. – agiro