C#:强制转换为基类型的通用接口
这是代码:
public interface IValidator { bool IsValid(T obj); } public class OrderValidator: IValidator { // ... } public class BaseEntity { } public class Order: BaseEntity { }
问题是我做不到:
var validator = new OrderValidator(); // this line throws because type can't be converted var baseValidator = (IValidator)validator; // all this is because I need a list with IValidator, IValidator, etc. IList<IValidator> allValidators = ...
如何获取并存储基础T的所有IValidator 实现的列表 – 比如BaseEntity? 目前我做的是非通用的IValidator,接受“对象obj”,但它不好,不是类型安全的。
有趣的是C# 允许编译:
var test = (IValidator)new OrderValidator();
但在运行时失败了
Unable to cast object of type 'OrderValidator' to type 'IValidator`1[Orders.Core.BaseEntity]'
这是Windsor给我的同样的exception(我试过Windsor和手动类型查找,这个问题实际上与此无关,只与接口转换有关)。
感谢Heinzi,我现在看到为什么我无法施放 – 因为IValidator for Order期望Order为generics类型。 但是,如何返回不同类型的IValidator列表? 原因是BaseEntity采用其真实类型并收集所有validation器 – 从GetType()到对象的所有类型。 我真的想要一个通用的GetValidators(),然后对它进行操作。
如果我解释为什么禁止这个演员,也许它可以帮助你:假设你有以下function
void myFunc(IValidator myValidator) { myValidator.IsValid(new BaseEntity()); }
这段代码可以正确编译。 但是,如果将OrderValidator
传递给此函数,则会得到运行时exception,因为OrderValidator.IsValid
需要Order而不是BaseEntity。 如果您的演员被允许,将不再保持类型安全。
编辑:C#4允许通用的共同和逆变 ,但这对你的情况没有帮助,因为你使用T作为输入参数。 因此,只能以类型安全的方式进行IValidator
。
因此,要清楚,您不能将OrderValidator
为IValidator
因为OrderValidator只能validation订单,而不能validation所有类型的BaseEntity。 然而,这是IValidator
所期望的。
IValidator
不起作用,因为IValidator
和IValidator
是完全不相关的类型。 IValidator
不是IValidator
的子类型,因此无法进行转换。
C#支持多接口inheritance,因此处理此问题的最简单方法是使订单validation器从两个validation器类型的接口inheritance,这样您就可以根据需要将其转换为任一接口。 显然,这意味着当提供的BaseEntity
与validation器的类型不匹配时,您必须实现两个接口并指定如何处理基数。
像这样的东西:
public class OrderValidator : IValidator, IValidator { public bool IsValid(Order obj) { // do validation // ... return true; } public bool IsValid(BaseEntity obj) { Order orderToValidate = obj as Order; if (orderToValidate != null) { return IsValid(orderToValidate); } else { // Eiter do this: throw new InvalidOperationException("This is an order validator so you can't validate objects that aren't orders"); // Or this: return false; // Depending on what it is you are trying to achive. } } }
这与Heinzi所说的无法投射有关,因为IValidator
需要能够validation您当前的OrderValidator
无法执行的BaseEntities
。 通过添加此多个接口,您可以显式定义用于validationBaseEntities
的行为(通过显式忽略它或导致exception),因此可以进行强制转换。
虽然这不会直接回答您,但我建议您查看StructureMap的源代码,它们可以使用开放的generics类型。 实际上甚至可能想使用StructureMap来处理validation器的缓存,这正是我所做的。
ForRequestedType(typeof (ValidationBase<>)).CacheBy(InstanceScope.Singleton); Scan(assemblies => { assemblies.TheCallingAssembly(); assemblies.AddAllTypesOf(typeof(IValidation<>)); });
然后我有一个工厂类来进行实际validation
public static class ValidationFactory { public static Result Validate(T obj) { try { var validator = ObjectFactory.GetInstance>(); return validator.Validate(obj); } catch (Exception ex) { ... } } }
编辑:我写了一篇关于使用IoC进行genericsvalidation的大博文,如果你看一下,因为你说你已经使用过Spring,我打赌你可以调整我的工作来解决你的问题: 创建一个通用的validation框架