【问题标题】:Best way to avoid code repetition using Generics and Func in c#在 C# 中使用泛型和 Func 避免代码重复的最佳方法
【发布时间】:2023-12-09 18:33:02
【问题描述】:

我想知道使用 Generics Func 或任何其他方式避免重复某些重复代码结构的最佳方法是什么。 作为一个实际的例子,我需要调用 20 个不同的 WCF 方法,但我希望有代码来处理异常。

假设这是 wcf 代理

 class ClassWithMethodsToCall // say wcf proxy
    {
        public Out1 GetOut1(In1 inParam) { return null; } // would have some spesific implementation 
        public Out2 GetOut2(In2 inParam) { return null; }
        public Out3 GetOut3(In3 inParam) { return null; }
    }

class Out1 { }  // some specific data structure
class In1 { }   // some specific data structure

class Out2 { }  // some specific data structure
class In2 { }   // some specific data structure

class Out3 { }  // some specific data structure
class In3 { }   // some specific data structure

我创建了以下内容以进行单一错误处理

class CallerHelperWithCommonExceptionHandler
    {
        public Tout Call<Tout, Tin>(Tin parameters, Func<Tin,Tout> wcfMethodToCall)
        {
            try
            {
                return wcfMethodToCall(parameters);
            }
            catch (Exception ex)
            {
                // do what ever
                throw;
            }
        }
    }

我使用它:

var callerHelper = new CallerHelperWithCommonExceptionHandler();
            var theFunctionsToCall = new ClassWithMethodsToCall();

        var in1 = new In1(); // init as appropriate
        var ou1 = callerHelper.Call<Out1, In1>(in1, theFunctionsToCall.GetOut1);

        var in2 = new In2(); // init as appropriate
        var ou2 = callerHelper.Call<Out2, In2>(in2, theFunctionsToCall.GetOut2);

        // and so on

有没有更好更优雅的方式?面向对象方式的替代方案,模板设计模式?

谢谢大家

【问题讨论】:

  • 这根本不是重复。你的班级ClassWithMethodsToCall 对我来说很好。你的 CallerHelperWithCommonExceptionHandler 让 IMO 变得更糟。
  • 同意,看起来更糟。
  • 你正在寻找的可能是面向方面的编程短 AOP 这里是一个例子ayende.com/blog/3474/logging-the-aop-way
  • 好吧,如果没有 CallerHelperWithCommonExceptionHandler,我必须为 ClassWithMethodsToCall 的每个方法调用重复 try catch。如果我有 20 种方法,我会为每个方法重复相同的错误处理代码块。

标签: c# generics func code-duplication


【解决方案1】:

您似乎正在向类添加代码以实现 cross-cutting concern(例如记录异常),因此您可能想要使用 decorator pattern

例如:

class Out1 { };  // some specific data structure
class In1 { }   // some specific data structure

class Out2 { }  // some specific data structure
class In2 { }   // some specific data structure

class Out3 { }  // some specific data structure
class In3 { }

internal interface IClassWithMethodsToCall
{
    Out1 GetOut1(In1 inParam);
    Out2 GetOut2(In2 inParam);
    Out3 GetOut3(In3 inParam);
}

class ClassWithMethodsToCallImpl: IClassWithMethodsToCall
{
    public Out1 GetOut1(In1 inParam) { return null; } // would have some spesific implementation 
    public Out2 GetOut2(In2 inParam) { return null; }
    public Out3 GetOut3(In3 inParam) { return null; }
}

class ClassWithMethodsToCall: IClassWithMethodsToCall
{
    private readonly ClassWithMethodsToCallImpl _impl;

    public ClassWithMethodsToCall(ClassWithMethodsToCallImpl impl)
    {
        _impl = impl;
    }

    public Out1 GetOut1(In1 inParam)
    {
        return tryFunc(() => _impl.GetOut1(inParam));
    }

    public Out2 GetOut2(In2 inParam)
    {
        return tryFunc(() => _impl.GetOut2(inParam));
    }

    public Out3 GetOut3(In3 inParam)
    {
        return tryFunc(() => _impl.GetOut3(inParam));
    }

    private static T tryFunc<T>(Func<T> func)
    {
        try
        {
            return func();
        }

        catch (Exception exception)
        {
            // Do something with exception
            throw;
        }
    }
}

客户端代码只会使用IClassWithMethodsToCall,您可能会在某处使用工厂方法来创建ClassWithMethodsToCallImpl,并使用它来创建ClassWithMethodsToCall,并将ClassWithMethodsToCall 作为IClassWithMethodsToCall 返回。

另外(可能更好)是使用Aspect-oriented programming。这需要更多的投资,并且可能需要使用第三方库来支持它,但从长远来看,这可能是要走的路。

我注意到您正在使用 WCF 代理。因为它使用 MarshalByRefObject,所以您可以利用它来实现 AOP。 There's some information about it in a blog here.

【讨论】:

  • 马修,你的例子很有道理。虽然我所理解的 AOP 更具体到异常、日志记录、...、常见操作。我正在寻找适合任何常见代码块的东西,例如这个 *.com/questions/13159326/… "1.Solution:" 似乎与我的相似 你觉得如何?
  • @user2418209 是的,这与我展示的private static T tryFunc&lt;T&gt;(Func&lt;T&gt; func) 基本相同——您可以使用该方法通过将重试放入tryFunc() 中来实现重试逻辑
  • @user2418209 AOP 不仅限于日志记录/异常处理,还可以实现缓存、事务处理等。只是需要拦截对对象方法的调用的所有内容。所以重试逻辑在 AOP 中也是可能的