【问题标题】:C# delegates with flexible signatures具有灵活签名的 C# 委托
【发布时间】:2012-04-26 13:25:19
【问题描述】:

我想做的是在各种对象之一上执行一个方法,但是在方法调用之前和之后,我需要执行函数来完成各种维护任务。之前和之后运行的函数总是相同的。方法的返回类型和参数几乎可以是任何东西。

目前,我通过为我正在使用的每个可能的方法签名定义委托来做到这一点,这样做变得非常尴尬。这是一个例子:

protected delegate void DelVoidString(string string1);
protected void Execute(DelVoidString p, string string1)
{
    PreInvoke();
    p.Invoke(string1);
    PostInvoke();
}

除了签名之外,所有签名上的代码都是相同的。所以我的问题是:有没有更好的方法来做到这一点?我不喜欢使用委托,只要我有一些方法可以在需要它们的方法调用之前和之后执行 PreInvoke() 和 PostInvoke(),而不必记住每次都写出该代码。

我对 Lambda 表达式只有模糊的了解……他们能在这里更好地帮助我吗?还是我错过了其他非常明显的东西?谢谢!

【问题讨论】:

    标签: c# delegates


    【解决方案1】:

    您可以使用泛型和内置的System.Action 委托类型来减少一些重复:

    protected void Execute(Action action)
    {
        PreInvoke();
        action();
        PostInvoke();
    }
    
    protected void Execute<T>(Action<T> action, T arg)
    {
        PreInvoke();
        action(arg);
        PostInvoke();
    }
    
    protected void Execute<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
    {
        PreInvoke();
        action(arg1, arg2);
        PostInvoke();
    }
    
    // Additional methods for three arguments, four arguments, etc.
    

    要处理返回值,请使用System.Func

    protected TResult Execute<TResult>(Func<TResult> func)
    {
        PreInvoke();
        TResult result = func();
        PostInvoke();
        return result;
    }
    
    protected TResult Execute<T, TResult>(Func<T, TResult> func, T arg)
    {
        PreInvoke();
        TResult result = func(arg);
        PostInvoke();
        return result;
    }
    

    您可能还想了解 C# 的面向方面编程 (AOP)。

    【讨论】:

    • 通用操作肯定会在一定程度上减少代码。我不知道它们可能是通用的。在少数情况下,返回值不是 void,但这种情况很少见。我刚刚发布后发现的另一个:Delegate.DynamicInvoke 呢?速度不是问题,那有可能吗?
    • 我更新了我的答案以处理返回值。 Delegate.DynamicInvoke 是可能的,但如果您传递的参数数量不正确,编译器将无法警告您。
    • 谢谢,我认为根据您的更改,这可能是最好的选择。
    【解决方案2】:

    提供其他人提供的主题的变体 - 您可以考虑创建一个通用抽象基类并像这样继承它:

    public abstract class Executable<T>
    {
        protected void Execute(Action<T> action, T value)
        {
            PreInvoke();
            action();
            PostInvoke();
        }
        private void PreInvoke() { /* something */ }
        private void PostInvoke() { /* something */ }
    }
    
    public MyFooExecutable : Executable<string>
    {
    }
    

    这种实现在MyFooExecutable 类上将泛型类型关闭为string。这将允许您拥有改变类型的其他子类,而不会为所有类型的 T 保留类型。根据您的 Pre/Post-Invoke 方法的作用,您甚至可以将它们放入扩展方法并编码到接口中:

    public interface IInvocable
    {
       void PreInvoke();
       void PostInvoke();
    }
    
    public class MyFooExecutable : IInvocable
    {
        public void PreInvoke() { /* something */ }
        public void PostInvoke() { /* something */ }
    }
    
    public static class InvocableExtensions
    {
        public static void Execute<T>(this IInvocable self, Action<T> action, T value)
        {
            self.PreInvoke();
            action(value);
            self.PostInvoke();
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果很多方法都需要这种前/后调用机制,我认为你应该考虑为.net 使用AOP 工具,例如PostSharp

      您可以在Dror Helper blog 或 PostSharp 网站Examples 下查看使用 PostSharp 的 OnMethodBoundaryAspect 的示例

      【讨论】:

        猜你喜欢
        • 2011-06-18
        • 1970-01-01
        • 2011-12-26
        • 2022-01-19
        • 2014-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多