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)
。然而,这也不会工作,因为我想动态加载,实例化和注册工人。
这是正确的方法还是有更好的方法来实现这一点?
我做了一些改变,但得到了一个解决方案,你可以保持东西通用(而不是使用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();
}