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

因此,要清楚,您不能将OrderValidatorIValidator因为OrderValidator只能validation订单,而不能validation所有类型的BaseEntity。 然而,这是IValidator所期望的。

IValidator不起作用,因为IValidatorIValidator是完全不相关的类型。 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框架