PostSharp:如何装饰返回的IEnumerable ?

问题描述 投票:1回答:2

如何让它工作?

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;
    }
}
postsharp
2个回答
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 )
    {
        // ...
    }
}

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;
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.