首页 > 解决方案 > PostSharp:如何装饰返回的 IEnumerable?

问题描述

如何让它工作?

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

解决方案


它看起来很难看,但它有效。如果 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;
        }
    }

推荐阅读