使用泛型进行多次调度
Posted
技术标签:
【中文标题】使用泛型进行多次调度【英文标题】:Multiple Dispatch with Generics 【发布时间】:2013-01-28 06:05:56 【问题描述】:我试图通过提供使用泛型的工厂/构建器来抽象出我的接口实现。但是,我在运行时遇到了多个调度和 C# 泛型的问题,这看起来很奇怪。
基本场景是我定义了几个接口:
public interface IAddressModel
public interface IUserModel
然后我有一个工厂类来返回实际的实现:
public class Factory
public T BuildModel<T>()
return BuildModel(default(T));
public object BuildModel(object obj)
//this is here because the compiler will complain about casting T to
//the first inteface parameter in the first defined BuildModel method
return null;
public IAddressModel BuildModel(IAddressModel iModel)
//where AddressModel inherits from IAddressModel
return new AddressModel();
public IUserModel BuildModel(IUserModel iModel)
//where UserModel inherits from IUserModel
return new UserModel();
问题是当工厂被这样调用时:new Factory().BuildModel<IAddressModel>()
在运行时从泛型分派的 BuildModel(...) 方法始终是 T 的最小派生形式,在这种情况下始终是对象。
但是,如果您调用new Factory().BuildModel(default(IAddressModel));
,则会调度正确的方法(很可能是因为这是在编译时完成的)。似乎使用泛型的动态分派不会检查最派生类型的方法,即使调用的方法应该是相同的,无论它是在编译时还是运行时完成。理想情况下,我想将 BuildModel(...) 方法设为私有,并且只公开通用方法。是否有另一种方法可以让动态调度在运行时调用正确的方法?我尝试将 BuildModel<>()
实现更改为 return BuildModel((dynamic)default(T))
但这会引发运行时错误,即无法确定要分派的方法。有没有办法通过逆变和更多接口来做到这一点?
【问题讨论】:
你考虑过new Factory<IAddressModel>().BuildModel()
吗?
@Bobson - 是的,我尝试将泛型移动到类级别而不是方法级别,这与运行时调度没有任何区别。
我不认为这是运行时与编译时的问题。据我所知,除非您使用dynamic
,否则泛型都在编译时解析,就像常规类型一样。而且您不能使用dynamic
,因为default(T)
将是null
,因此它无法确定它应该是什么。我怀疑这就是为什么工厂的标准是使用switch
...
【参考方案1】:
您也许可以根据参数类型 T
自己进行调度:
public class Factory
private Dictionary<Type, Func<object>> builders = new Dictionary<Type, Func<object>>
typeof(IAddressModel), BuildAddressModel ,
typeof(IUserModel), BuildUserModel
;
public T Build<T>()
Func<object> buildFunc;
if (builders.TryGetValue(typeof(T), out buildFunc))
return (T)buildFunc();
else throw new ArgumentException("No builder for type " + typeof(T).Name);
private static IAddressModel BuildAddressModel()
return new AddressModel();
private static IUserModel BuildUserModel()
return new UserModel();
【讨论】:
【参考方案2】:代码的当前状态需要显式转换才能编译。
public T BuildModel<T>()
return (T)BuildModel(default(T));
BuildModel 将 T 多态地视为一个对象。 BuildModel
不知道 T 是 IAddressModel
或 IUserModel
,除非您定义了这样的限制:
public T BuildModel<T>() where T: IAddressModel
Console.WriteLine(typeof(T));
return (T)BuildModel(default(T));
现在,编译器有足够的信息来识别 T 是 IAddressModel
。但是,您所追求的是让object
成为一个更加派生的参数(协变量),它不是类型安全的。换句话说,C# 不支持协变参数类型,因为它不是类型安全的。
您仍然可以通过条件逻辑实现类似工厂的行为:
public T BuildModel<T>()
T result = default(T);
if (typeof(T) == typeof(IAddressModel))
result = (T)BuildModel((IAddressModel)result);
else if (typeof(T) == typeof(IUserModel))
result = (T)BuildModel((IUserModel)result);
return result;
【讨论】:
以上是关于使用泛型进行多次调度的主要内容,如果未能解决你的问题,请参考以下文章