按特定属性

时间:2017-08-03 12:02:45

标签: c# linq delegates attributes invoke

我已经为一个事件订阅了一些方法,并希望按照我给他们的具体顺序调用它们:

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

其中(当然)事件为LOAD_DEPENDENCIES且属性为InvocationOrderAttibute。 {strong>注意 foreach的正文有效,DynamicInvoke的参数不是问题。

该属性如下:

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);
    }
}

我实施了IComparable希望OrderBy将使用它来确定订单。

这不起作用,通过调试我检查了我甚至从未输入foreach循环的主体。 ALL 订阅的方法具有该属性。

问题是,我在foreach循环的LINQ查询或属性中做错了什么?

修改

它是最好的,但有效:

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

1 个答案:

答案 0 :(得分:1)

.GetCustomAttributes会返回object[],因此.OrderBy将使用Comparer<object[]>.Default,因为它想要使用某些IComparable实现,因此会向您抛出异常键入object[],它不存在。

相反,你应该使用.GetCustomAttribute<InvocationOrderAttribute>,它返回属性,如果没有这样的属性,则使用null。没有属性的方法与具有属性的方法相比如何?

刚刚编写了一个小例子,它不使用事件处理程序以及不带有属性的事件处理程序,后者在排序中位于前者之前。该示例的代理类型为Action(您的是其他内容)。

编辑:不支持CustomAttributeExtensions

的支持C#4.0的版本

属性:

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

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

新:适用于所有类型事件的有用方法

/// <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);
}

用法:

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
    }
}

输出:M1 M1 M2 M2 M3 M3

对代码的改进(包括第二个解决方案):

  • 如果没有注册处理程序,则为NullReferenceException
  • 如果某个处理程序没有属性,则为NullReferenceException
  • 在排序期间未反映属性。
  • 适用于所有活动代表类型。
  • .DynamicIvoke(丑陋,不重构友好,效率低下)
  • (不使用标准的标识符框来调用委托“方法”。)