如何使其正常工作?
public override void OnExit(MethodExecutionArgs args) {
var enumerable = (IEnumerable) args.ReturnValue;
return Log(
enumerable, // How to cast to unknown generic type?
() => logger.LogRequestEntry(),
stopwatch => logger.LogRequestExit( stopwatch ),
ex => logger.LogRequestError( ex ) );
}
private static IEnumerable<T> Log<T>(IEnumerable<T> enumerable, Func<Stopwatch> logEntry, Action<Stopwatch> logExit, Action<Exception> logError) {
var stopwatch = logEntry();
try {
using (var enumerator = enumerable.GetEnumerator()) {
while (MoveNext( enumerator, logError )) yield return enumerator.Current;
}
} finally {
logExit( stopwatch );
}
}
private static bool MoveNext<T>(IEnumerator<T> enumerator, Action<Exception> logError) {
try {
return enumerator.MoveNext();
} catch (Exception ex) {
logError( ex );
throw;
}
}
答案 0 :(得分:1)
它看起来很丑,但是可以用。 如果C#支持将静态成员动态化,那会更好,但是现在我们只能使用反射来调用静态方法。
private static readonly MethodInfo LogMethod = typeof( LogEnumerableRequestAttribute ).GetMethod( nameof( Log_ ), BindingFlags.NonPublic | BindingFlags.Static );
public static IEnumerable Log(IEnumerable enumerable, Func<Stopwatch> logEntry, Action<Stopwatch> logExit, Action<Exception> logError) {
var @interface = enumerable.GetType().GetInterfaces().First( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof( IEnumerable<> ) );
var generic = @interface.GetGenericArguments().First();
var method = LogMethod.MakeGenericMethod( generic );
return (IEnumerable) method.Invoke( null, new object[] { enumerable, logEntry, logExit, logError } );
}
private static IEnumerable<T> Log_<T>(IEnumerable<T> enumerable, Func<Stopwatch> logEntry, Action<Stopwatch> logExit, Action<Exception> logError) {
var stopwatch = logEntry();
try {
using (var enumerator = enumerable.GetEnumerator()) {
while (MoveNext( enumerator, logError )) yield return enumerator.Current;
}
} finally {
logExit( stopwatch );
}
}
private static bool MoveNext<T>(IEnumerator<T> enumerator, Action<Exception> logError) {
try {
return enumerator.MoveNext();
} catch (Exception ex) {
logError( ex );
throw;
}
}
答案 1 :(得分:1)
您需要使用反射来构造正确的泛型类型的返回值。但是,您可以将所有反射代码移至构建时逻辑,并提高运行时性能。您可以在构建时构造方面的通用实例,如下所示。
[PSerializable]
public class LogEnumerableRequestAttribute : MethodLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
{
var @interface = ( (MethodInfo) targetElement ).ReturnParameter.ParameterType;
if (!IsGenericIEnumerable(@interface))
{
@interface = @interface.GetInterfaces().First( IsGenericIEnumerable );
}
var generic = @interface.GetGenericArguments().First();
var aspectGenericType = typeof( LogEnumerableRequestImpl<> ).MakeGenericType( generic );
yield return new AspectInstance(
targetElement, (IAspect) Activator.CreateInstance( aspectGenericType ) );
}
private static bool IsGenericIEnumerable( Type t )
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof( IEnumerable<> );
}
}
[PSerializable]
public class LogEnumerableRequestImpl<T> : IMethodLevelAspect
{
[OnMethodExitAdvice(SemanticallyAdvisedMethodKinds = SemanticallyAdvisedMethodKinds.None)]
[SelfPointcut]
public void OnMethodExit( MethodExecutionArgs args )
{
args.ReturnValue = Log_( (IEnumerable<T>) args.ReturnValue );
}
public void RuntimeInitialize( MethodBase method )
{
}
private static IEnumerable<T> Log_<T>( IEnumerable<T> enumerable, Func<Stopwatch> logEntry, Action<Stopwatch> logExit, Action<Exception> logError )
{
// ...
}
private static bool MoveNext<T>( IEnumerator<T> enumerator, Action<Exception> logError )
{
// ...
}
}