c#:持有处理程序对象的集合并找到正确的处理程序对象

本文关键字:程序 处理 对象 合并 集合 | 更新日期: 2023-09-27 18:04:21

所以我有一个名为IWorkItem的接口,它在WorkA, WorkB和许多其他类中实现。

public interface IWorker<T> where T : IWorkItem
{
    void Process(T item);
}

IWorker<T>接口在WorkerA (IWorker<WorkA>), WorkerB (IWorker<WorkB>)和许多其他类中实现。

public static void ProcessWorkItem(IWorkItem item)
{
    (/* find the right worker */).Process(item);
}

现在我的问题是:如何找到一个能够处理给定IWorkItem的工作对象?

我的第一次尝试看起来像这样,但是泛型类型参数是一个问题:

public static class WorkerRepository
{
    private static Dictionary<Type, IWorker<???>> RegisteredWorkers =
        new Dictionary<Type, IWorker<???>>();
    public static void RegisterWorker(IWorker<???> worker)
    {
        var handled = from iface in worker.GetType().GetInterfaces()
                      where iface.IsGenericType
                      where iface.GetGenericTypeDefinition() == typeof(IWorker<>)
                      select iface.GetGenericArguments()[0];
        foreach (var type in handled)
            if (!RegisteredWorkers.ContainsKey(type))
                RegisteredWorkers[type] = worker;
    }
    public static void ProcessWorkItem(IWorkItem item)
    {
        RegisteredWorkers[item.getType()].Process(item);
    }
}

所以我有包含工人的Dictionary。这里需要哪个类型参数?在Java中,我可以使用? extends IWorkItem,但我在c#中这样做吗?然后是RegisterWorker。您可能会建议为整个方法使用泛型类型参数,如RegisterWorker<T>(IWorker<T> worker)。然而,这也不会工作,因为我想动态加载,实例化和注册工人。

这是正确的方法还是有更好的方法来实现这一点?

c#:持有处理程序对象的集合并找到正确的处理程序对象

我做了一些改变,但得到了一个解决方案,你可以保持东西通用(而不是使用object s)。不确定你是否关心,但想把它添加为答案,让你决定。

我还写了一个测试来检查它是否实际工作,你应该能够复制/粘贴它。

[TestFixture]
public class WorkerThing
{
    [Test]
    public void RegisterAndRetrieveWorkers()
    {
        var repo = new WorkerRepository();
        repo.RegisterWorker(new WorkerA());
        var workerA = repo.RetrieveWorkerForWorkItem(new WorkItemA());
        Assert.IsTrue(workerA is WorkerA);
        repo.RegisterWorker(new WorkerB());
        var workerB = repo.RetrieveWorkerForWorkItem(new WorkItemB());
        Assert.IsTrue(workerB is WorkerB);
    }
}

WorkerRepository类。

public class WorkerRepository
{
    private readonly Dictionary<Type, IWorker<IWorkItem>> _registeredWorkers =
        new Dictionary<Type, IWorker<IWorkItem>>();
    public void RegisterWorker(IWorker<IWorkItem> worker)
    {
        var type = (from iface in worker.GetType().GetInterfaces()
                      where iface.IsGenericType
                      where iface.GetGenericTypeDefinition() == typeof(IWorker<>)
                      select iface.GetGenericArguments()[0]).First();
        if (!_registeredWorkers.ContainsKey(type))
        {
            _registeredWorkers[type] = worker;
        }
    }
    // You don't need this method, just added it to check if I indeed retrieved the correct type
    //
    public IWorker<IWorkItem> RetrieveWorkerForWorkItem(IWorkItem item)
    {
        var type = item.GetType();
        var registeredWorker = _registeredWorkers[type];
        return registeredWorker;
    }
    public void ProcessWorkItem(IWorkItem item)
    {
        var type = item.GetType();
        var registeredWorker = _registeredWorkers[type];
        registeredWorker.Process(item);
    }
}

工作项接口&类。

public interface IWorkItem
{
}
public class WorkItemA : IWorkItem
{
}
public class WorkItemB : IWorkItem
{
}

我在这里添加了out关键字,以允许在接口上进行协方差输入。这样就可以将WorkerA转换为IWorker<IWorkItem>。(如在单元测试示例中)

public interface IWorker<out T> where T : IWorkItem
{
    void Process(IWorkItem workItem);
}
public class WorkerA : IWorker<WorkItemA>
{
    public void Process(IWorkItem item)
    {
    }
}
public class WorkerB : IWorker<WorkItemB>
{
    public void Process(IWorkItem item)
    {
    }
}

没有object字典。没有反射。我希望这个例子是有用的!

干杯(谢谢你的酷问题,它让我忙了一段时间:))

看起来你想要这样做:

private static Dictionary<Type, object> RegisteredWorkers = new Dictionary<Type, object>();
public static void RegisterWorker(object worker)
{
    var handled = from iface in worker.GetType().GetInterfaces()
                  where iface.IsGenericType
                  where iface.GetGenericTypeDefinition() == typeof(Worker<>)
                  select iface.GetGenericArguments()[0];
    foreach (var type in handled)
        if (!RegisteredWorkers.ContainsKey(type))
            RegisteredWorkers[type] = worker;
}
public static void ProcessWorkItem(WorkItem item)
{
    object handler = RegisteredWorkers[item.getType()];
    Type workerType = typeof(Worker<>).MakeGenericType(item.GetType());
    MethodInfo processMethod = workerType.GetMethod("Process");
    processMethod.Invoke(handler, new object[] { item });
}

如果你不想每次都通过反射调用处理程序,你可以在注册处理程序时生成一个Action<IWorkItem>处理程序:

public void RegisterHandler(object handler)
{
    var handled = from iface in handler.GetType().GetInterfaces()
                  where iface.IsGenericType
                  where iface.GetGenericTypeDefinition() == typeof(IWorker<>)
                  select iface.GetGenericArguments()[0];
    foreach (var type in handled)
    {
        if (!RegisteredWorkers.ContainsKey(type))
        {
            Action<IWorkItem> handleAction = HandlerAction(type, handler);
            RegisteredWorkers[type] = handleAction;
        }
    }   
}
public void Process(IWorkItem item)
{
    Action<IWorkItem> handleAction = RegisteredWorkers[item.GetType()];
    handleAction(item);
}
private static Action<IWorkItem> HandlerAction(Type itemType, object handler)
{
    var paramExpr = Expression.Parameter(typeof(IWorkItem));
    var castExpr = Expression.Convert(paramExpr, itemType);
    MethodInfo processMethod = typeof(IWorker<>).MakeGenericType(itemType).GetMethod("Process");
    var invokeExpr = Expression.Call(Expression.Constant(handler), processMethod, castExpr);
    var lambda = Expression.Lambda<Action<IWorkItem>>(invokeExpr, paramExpr);
    return lambda.Compile();
}