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

如何实现两个独立Partial Class同名方法协同输出且无依赖?

Alternative Approaches for Combining Partial Class Method Results

Great question! The requirement to dynamically combine results from multiple partial classes (and gracefully handle their removal) is a common scenario, and there are a few clean approaches beyond the event + dependency injection combo you already have in mind. Let's break them down with code examples and pros/cons.

1. Interface + Dependency Injection (Explicit Implementation Registration)

This is a more structured take on DI that leans on abstraction instead of events. The idea is to define a common interface for your DoFoo logic, have each partial class implement it, then inject all implementations into your parent class.

Example Code:

First, define the interface:

public interface IFooHandler
{
    string DoFoo();
}

Then your partial classes:

public partial class FooHandlerA : IFooHandler
{
    public string DoFoo() => "1234";
}

public partial class FooHandlerB : IFooHandler
{
    public string DoFoo() => "5678";
}

Your parent class injects all instances of IFooHandler and combines results:

public class ParentClass
{
    private readonly IEnumerable<IFooHandler> _fooHandlers;

    public ParentClass(IEnumerable<IFooHandler> fooHandlers)
    {
        _fooHandlers = fooHandlers;
    }

    public string CombineFooResults()
    {
        return string.Concat(_fooHandlers.Select(h => h.DoFoo()));
    }
}

How it fits your requirement:

  • When you register both FooHandlerA and FooHandlerB in your DI container, CombineFooResults returns "12345678".
  • Remove one handler from your DI registration (or delete the class entirely) and the method will only return the remaining handler's result.

Pros & Cons:

  • ✅ Follows SOLID principles (dependency inversion, single responsibility)
  • ✅ Easy to test (mock IFooHandler instances)
  • ❌ Requires a DI container (though you could manually build the list if needed)

2. Static Delegate Registration (No DI Required)

If you want a lighter-weight approach without DI, you can use static delegates to let each partial class register its DoFoo method on startup.

Example Code:

First, create a static registry in your parent class (or a separate helper):

public static class FooHandlerRegistry
{
    public static List<Func<string>> RegisteredHandlers { get; } = new List<Func<string>>();
}

Then each partial class registers itself in its static constructor:

public partial class FooHandlerA
{
    static FooHandlerA()
    {
        FooHandlerRegistry.RegisteredHandlers.Add(DoFoo);
    }

    public static string DoFoo() => "1234";
}

public partial class FooHandlerB
{
    static FooHandlerB()
    {
        FooHandlerRegistry.RegisteredHandlers.Add(DoFoo);
    }

    public static string DoFoo() => "5678";
}

Your parent class uses the registry to combine results:

public class ParentClass
{
    public string CombineFooResults()
    {
        return string.Concat(FooHandlerRegistry.RegisteredHandlers.Select(handler => handler()));
    }
}

How it fits your requirement:

  • When both classes exist, their static constructors run on first access, adding their methods to the registry.
  • Delete one class, and its registration never happens—so only the remaining handler's result is used.

Pros & Cons:

  • ✅ No DI container needed
  • ✅ Very lightweight
  • ❌ Static state can be tricky to test (you may need to clear the registry between tests)
  • ❌ Thread safety: if handlers are registered concurrently, you'll need to use a thread-safe collection like ConcurrentBag<Func<string>>

3. Reflection-Based Scanning (Dynamic Discovery)

Another option is to use reflection to automatically find all classes that implement your DoFoo logic (you can use a custom attribute to mark them for easy discovery).

Example Code:

First, create a custom attribute to mark your handlers:

[AttributeUsage(AttributeTargets.Class)]
public class FooHandlerAttribute : Attribute { }

Mark your partial classes with the attribute:

[FooHandler]
public partial class FooHandlerA
{
    public string DoFoo() => "1234";
}

[FooHandler]
public partial class FooHandlerB
{
    public string DoFoo() => "5678";
}

Your parent class scans the assembly for marked classes, instantiates them, and calls DoFoo:

public class ParentClass
{
    public string CombineFooResults()
    {
        var result = new StringBuilder();
        var assembly = Assembly.GetExecutingAssembly();
        
        // Find all classes with FooHandlerAttribute
        var handlerTypes = assembly.GetTypes()
            .Where(t => t.GetCustomAttribute<FooHandlerAttribute>() != null);

        foreach (var type in handlerTypes)
        {
            var handler = Activator.CreateInstance(type);
            var doFooMethod = type.GetMethod("DoFoo");
            if (doFooMethod != null)
            {
                var fooResult = doFooMethod.Invoke(handler, null) as string;
                result.Append(fooResult);
            }
        }

        return result.ToString();
    }
}

How it fits your requirement:

  • Reflection finds all marked classes at runtime—if you delete one, it won't show up in the scan.
  • No manual registration needed; just mark classes with the attribute.

Pros & Cons:

  • ✅ No DI or manual registration
  • ✅ Fully dynamic (add/remove classes without changing parent code)
  • ❌ Reflection has a small performance overhead (negligible for most scenarios, but worth noting)
  • ❌ Tighter coupling (relies on method names/attributes instead of interfaces)

Which Approach Should You Choose?

  • If you're already using a DI container (like ASP.NET Core's built-in one), go with the Interface + DI approach—it's the most maintainable and testable.
  • If you want something lightweight without DI, the Static Delegate Registration is a solid choice (just watch thread safety).
  • If you need full dynamic discovery (no registration steps at all), Reflection-Based Scanning works, but be aware of the tradeoffs.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:55:12