PostSharp:如何装饰返回的IEnumerable <t>?

时间:2019-02-05 21:40:03

标签: postsharp

如何使其正常工作?

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

2 个答案:

答案 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 )
    {
        // ...
    }
}