3

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

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). 但是,这也行不通,因为我想动态加载、实例化和注册 Workers。

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

4

2 回答 2

2

我做了一些更改,但得到了一个解决方案,您可以在其中保持通用(而不是使用objects)。不确定您是否关心,但想将其添加为答案并让您决定。

我还写了一个测试来检查它是否真的有效,你应该可以复制/粘贴它。

[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关键字以允许在界面上键入协方差。这样你就可以转换WorkerAIWorker<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字典。没有反射。我希望这个例子是有用的!

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

于 2013-03-16T22:37:08.340 回答
1

看起来你想要这样的东西:

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();
}
于 2013-03-16T20:57:26.247 回答