特定属性的调用顺序
Invocation order by a specific attribute
我已经为一个事件订阅了一些方法,并希望按照我给它们的特定顺序调用它们,如下所示:
foreach (var method in LOAD_DEPENDENCIES.GetInvocationList()
.OrderBy(x => x.Method.GetCustomAttributes(typeof(InvocationOrderAttribute),false)))
{
method.DynamicInvoke(localStats, this);
}
其中(当然)事件是 LOAD_DEPENDENCIES
,属性是 InvocationOrderAttibute
。 注意 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);
}
.GetCustomAttributes
returns an object[]
, so .OrderBy
will use Comparer<object[]>.Default
, that will throw exceptions at you as it wants to use some IComparable
implementation on the type object[]
, which does not exist.
Instead you should use .GetCustomAttribute<InvocationOrderAttribute>
, which returns the attribute or null if no such attribute is present. How do methods without the attribute compare to those having one?
Just wrote a small example that works with no event handlers as well as with event handlers not carrying the attribute, where the latter precede the former in the ordering. The event is of delegate type Action
in the example (yours is something else).
EDIT: C# 4.0 capable version without CustomAttributeExtensions
The attribute:
[AttributeUsage(AttributeTargets.Method)]
public sealed class InvocationOrderAttribute : Attribute
{
public int Order { get; private set; }
public InvocationOrderAttribute(int order)
{
Order = order;
}
}
New: useful method for all types of events
/// <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);
}
Usage:
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
}
}
Output: M1 M1 M2 M2 M3 M3
The improvements over your code (including 2nd solution):
- No
NullReferenceException
if no handlers registered.
- No
NullReferenceException
if some handler has no attribute.
- Not reflecting the attribute during the sort.
- Applicable for all event delegate types.
- No
.DynamicIvoke
(ugly, not refactoring friendly, inefficient)
- (Not calling a delegate "method", using standard casing of identifiers.)
我已经为一个事件订阅了一些方法,并希望按照我给它们的特定顺序调用它们,如下所示:
foreach (var method in LOAD_DEPENDENCIES.GetInvocationList()
.OrderBy(x => x.Method.GetCustomAttributes(typeof(InvocationOrderAttribute),false)))
{
method.DynamicInvoke(localStats, this);
}
其中(当然)事件是 LOAD_DEPENDENCIES
,属性是 InvocationOrderAttibute
。 注意 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);
}
.GetCustomAttributes
returns an object[]
, so .OrderBy
will use Comparer<object[]>.Default
, that will throw exceptions at you as it wants to use some IComparable
implementation on the type object[]
, which does not exist.
Instead you should use .GetCustomAttribute<InvocationOrderAttribute>
, which returns the attribute or null if no such attribute is present. How do methods without the attribute compare to those having one?
Just wrote a small example that works with no event handlers as well as with event handlers not carrying the attribute, where the latter precede the former in the ordering. The event is of delegate type Action
in the example (yours is something else).
EDIT: C# 4.0 capable version without CustomAttributeExtensions
The attribute:
[AttributeUsage(AttributeTargets.Method)]
public sealed class InvocationOrderAttribute : Attribute
{
public int Order { get; private set; }
public InvocationOrderAttribute(int order)
{
Order = order;
}
}
New: useful method for all types of events
/// <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);
}
Usage:
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
}
}
Output: M1 M1 M2 M2 M3 M3
The improvements over your code (including 2nd solution):
- No
NullReferenceException
if no handlers registered. - No
NullReferenceException
if some handler has no attribute. - Not reflecting the attribute during the sort.
- Applicable for all event delegate types.
- No
.DynamicIvoke
(ugly, not refactoring friendly, inefficient) - (Not calling a delegate "method", using standard casing of identifiers.)