【发布时间】:2014-10-24 14:53:53
【问题描述】:
我有两个这样的通用接口:
public interface IFoo<T> { }
public interface IBar<TFoo, T> where TFoo : Foo<T> {
T Qux(TFoo foo);
}
IFoo<T> 有一堆实现,每个都有一个匹配的实现 IBar<S,T>,注册在我的 IoC 容器(恰好是 Castle.Windsor)中。
现在,我想创建一个方法,该方法基于实现IFoo<T> 的某种类型的参数,从Windsor 返回相应的IBar<IFoo<T>,T>。一个简单的实现如下所示:
public IBar<TFoo,T> GetBarFor(IFoo<T> foo) where TFoo : IFoo<T>
{
return container.Resolve<IBar<TFoo,T>>();
}
但是,在调用代码中,我没有具体输入 Foo<T> - 只是一个 IFoo<T>:
public T DoStuffWithStuff<T>(IFoo<T> foo)
{
var bar = GetBarFor(foo);
return bar.Qux(foo);
}
并且在这个调用中,编译器无法推断出类型参数(具体来说,我猜是TFoo),所以我需要采取另一种方法。
我尝试使用Resolve() 的非泛型版本解析类型,但无法正确获取返回类型:
public AbstractBar<T> GetBarFor(IFoo<T> foo) where TFoo : IFoo<T>
{
var barType = typeof(IBar<,>).MakeGenericType(foo.GetType(),typeof(T));
var bar = container.Resolve(barType); // bar is now a System.Object
var wrapperType = typeof(BarWrapper<,>).MakeGenericType(foo.GetType(), typeof(T));
var wrapper = Activator.CreateInstance(wrapperType, bar);
return (AbstractBar<T>)wrapper;
}
与
public abstract class AbstractBar<T> { public abstract T Qux(IFoo<T> foo); }
public class BarWrapper<TFoo, T> : AbstractBar<T> where TFoo : IFoo<T>
{
private IBar<TFoo,T> _inner;
public BarWrapper(IBar<TFoo,T> inner) { _inner = inner; }
public T Qux(IFoo<T> foo) { return _inner.Qux((TFoo)foo); }
}
但我在Activator.CreateInstance 调用中不断收到异常,说找不到BarWrapper 的构造函数(是的,一切都是public)。
有没有一种好方法可以让上述DoStuffWithStuff 中的调用完全适用于该签名(即我不能从调用代码中推迟任何更具体的泛型)?
脚注:我在Jimmie Bogard's MediatR implementation of the mediator pattern 中看到了后一种模式的工作原理,实际上我在这里尝试完成的是一个非常相似的实现,它不依赖于公共服务定位器,但是而是直接进入我们的 IoC 容器。在上下文中,IFoo<T> 真的是IRequest<out TResponse>,IBar<TFoo, T> 真的是IRequestHandler<in TRequest, out TResponse>,等等。
查看他的代码,并尝试重新创建它,但通过调用 Windsor 容器而不是服务定位器,我无法弄清楚为什么他的代码有效,而我的却没有。
【问题讨论】:
-
旁注:与其说“是的,一切都是公开的”,不如显示正确的代码,例如
public BarWrapper(... -
类 BarWrapper { BarWrapper(); ... } 你确定构造函数是公开的吗?
-
您是否尝试过让
GetBarFor的非反射版本首先工作? (指定类型而不是foo.GetType()并让它编译/运行) -
@AlexeiLevenkov 和 Igor:我试图为自己节省一些打字时间,但我知道为什么这会令人困惑。我更新了代码示例以反映实际代码。
-
@AlexeiLevenkov:是的,我试过了,并且让它工作了,但不幸的是,它要求我一直提供类型参数,直到调用
DoStuffWithStuff,这是我无法拥有的(它会打败将 IoC 容器注入我的调解器的目的......)。我还没有找到一种方法来让它工作,它既不需要 a) 在最外层调用中需要类型参数,也不需要 b) 使用反射。
标签: c# generics reflection type-inference