我已经为一个事件订阅了一些方法,并希望按照我给他们的具体顺序调用它们:
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);
}
答案 0 :(得分:1)
.GetCustomAttributes
会返回object[]
,因此.OrderBy
将使用Comparer<object[]>.Default
,因为它想要使用某些IComparable
实现,因此会向您抛出异常键入object[]
,它不存在。
相反,你应该使用.GetCustomAttribute<InvocationOrderAttribute>
,它返回属性,如果没有这样的属性,则使用null。没有属性的方法与具有属性的方法相比如何?
刚刚编写了一个小例子,它不使用事件处理程序以及不带有属性的事件处理程序,后者在排序中位于前者之前。该示例的代理类型为Action
(您的是其他内容)。
编辑:不支持CustomAttributeExtensions
属性:
[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
(丑陋,不重构友好,效率低下)