You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合并Action与Action<T>重载函数以消除重复代码?

Great question! You're right that Action and Action<T> don't share a specific interface beyond Delegate, but there are a few ways to consolidate these two methods into a single generic implementation while avoiding duplicate code. Let's break down your options:

Option 1: Use Delegate with Pattern Matching (C# 7.0+)

You can accept a base Delegate parameter and use pattern matching to handle both Action and Action<T> cases in one method. This keeps all your logic in a single place:

private void CallVoidFunction<T>(Delegate action, object property = null)
{
    // Your shared pre-invocation code here
    Console.WriteLine("Pre-invocation common code");

    switch (action)
    {
        case Action noParamAction:
            noParamAction.Invoke();
            break;
        case Action<T> paramAction when property != null:
            paramAction.Invoke((T)property);
            break;
        default:
            throw new ArgumentException("Unsupported action type", nameof(action));
    }

    // Your shared post-invocation code here
    Console.WriteLine("Post-invocation common code");
}

Usage:

// Call with Action
CallVoidFunction<object>(() => Console.WriteLine("No param action"));

// Call with Action<int>
CallVoidFunction<int>((num) => Console.WriteLine($"Param action: {num}"), 42);

Note: You need to specify the generic type parameter when calling (even object for the no-param case), but this consolidates your duplicate logic into one method.

Option 2: Wrap Action as Action<T> (Simpler Invocation)

If you want to avoid specifying the generic type for no-param actions, you can create a wrapper that converts an Action to an Action<T> (ignoring the parameter), then use a single generic method:

private void CallVoidFunction<T>(Action<T> action, object property = null)
{
    // Shared pre-invocation code
    Console.WriteLine("Pre-invocation common code");

    if (typeof(T) == typeof(EmptyParam) && property == null)
    {
        // Unwrap the no-param action
        ((Action)(Delegate)action).Invoke();
    }
    else
    {
        action.Invoke((T)property);
    }

    // Shared post-invocation code
    Console.WriteLine("Post-invocation common code");
}

// Helper marker type for no-param cases
private class EmptyParam { }

// Extension to wrap Action as Action<EmptyParam>
private static Action<EmptyParam> WrapNoParamAction(Action action)
{
    return _ => action.Invoke();
}

Usage:

// Call with Action (using wrapper)
CallVoidFunction(WrapNoParamAction(() => Console.WriteLine("No param action")));

// Call with Action<int>
CallVoidFunction((num) => Console.WriteLine($"Param action: {num}"), 42);

This makes invocation cleaner for both cases, though it requires a small helper type and wrapper method.

Comparing to Your ActionContainer Approach

Your current ActionContainer works, but it's a bit verbose since you have to instantiate a container object every time. The options above keep the logic in a single method without needing extra object instantiation.

A Compromise: Extract Common Code (Still Clean!)

While you mentioned you know you can extract common code, it's worth highlighting that this is often the most idiomatic approach in C#—it keeps your method signatures clean and leverages overloads (which are designed for exactly this scenario):

private void CallVoidFunction(Action action, object property = null)
{
    RunCommonPreCode(property);
    action.Invoke();
    RunCommonPostCode(property);
}

private void CallVoidFunction<T>(Action<T> action, object property = null)
{
    RunCommonPreCode(property);
    action.Invoke((T)property);
    RunCommonPostCode(property);
}

// Shared code in private methods
private void RunCommonPreCode(object property)
{
    // Your duplicate pre-invocation logic here
}

private void RunCommonPostCode(object property)
{
    // Your duplicate post-invocation logic here
}

This maintains the type safety and clean invocation of your original overloads while eliminating duplicate code entirely. It's often the preferred approach because it's straightforward and aligns with C# best practices.

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:27:28