【问题标题】:Factory to create generic classes工厂创建泛型类
【发布时间】:2012-03-09 10:31:42
【问题描述】:

我有一个名为 Validator 的抽象类:

public abstract class Validator<T> where T : IValidatable
{
    public abstract bool Validate(T input);
}

我有一些具体的实现。一个是AccountValidator:

public class AccountCreateValidator : Validator<IAccount>
{
    public override bool Validate(IAccount input)
    {
        //some validation
    }
}

另一个是 LoginValidator:

public class LoginValidator : Validator<IAccount>
{
    public override bool Validate(IAccount input)
    {
        //some different validation
    }
}

我现在想创建一个工厂来返回验证器实现的实例。比如:

public static class ValidatorFactory
{
    public static Validator GetValidator(ValidationType validationType)
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator();
        }
    }
}

然后我想这样称呼它

Validator myValidator = ValidatorFactory.GetValidator(ValidationType.AccountCreate); 

但是它不喜欢 return new AccountCreateValidator() 行,或者我将 myValidator 声明为 Validator 而不是 Validator&lt;SomeType&gt;。 任何帮助将不胜感激。

【问题讨论】:

  • 您获得的代码当前无效 - 可以到达方法的结尾。请展示一个简短但完整的程序来演示该问题。
  • 基于IValidatable 的类型而不是基于枚举的验证器不是更好吗?然后你可以返回一个Validator&lt;T&gt; 并在GetValidator 方法中采用T 并且T 可以被限制为IValidatable
  • class Validator&lt;T&gt; where T : IValidatable,你不就是想要class Validator : IValidatable吗?
  • 抱歉,之前不清楚。我想为同一个 IValidatable 设置多个验证器。例如,AccountCreateValidator 和 LoginValidator 都将针对 IAccount 进行验证

标签: c# generics factory-pattern fluentvalidation


【解决方案1】:

您似乎正在使用工厂将枚举参数转换为具体的验证实现。但我可以想象,虽然调用者不知道或不关心验证器的具体类型,但它大概知道它希望验证的类型。这应该意味着将 GetValidator 方法设为泛型方法是合理的:

public static Validator<TypeToValidate> GetValidator<TypeToValidate>(ValidationType validationType) where TypeToValidate : IValidatable

然后调用代码将如下所示: Validator&lt;IAccount&gt; validator = ValidatorFactory.GetValidator&lt;IAccount&gt;(ValidationType.AccountCreate)

【讨论】:

  • +1 这是一个更好的方法,只要你知道你要验证什么类型
  • 你说得对,传入我们要验证的类型就足够公平了。你能告诉我如何返回验证器的实例吗?更改为您的代码,但编译器对我的返回语句不满意:return new AccountCreateValidator();
  • 问题在于您返回的验证器仅对特定类型的 TypeToValidate 有效。我认为您要么想为单一类型的 TypeToValidate 创建此工厂,即使其成为 AcountValidator 工厂,或者如果您想使其通用,则使整个工厂在 TypeToValidate 上通用,并具有针对 ValidationType 值注册具体验证器的机制.
  • 这是有道理的。好的,我将创建多个工厂。感谢您对这些人的帮助
【解决方案2】:

如果你想像你说的那样使用它,而不指定泛型参数,那么你可以声明一个非泛型接口并让你 Validator 抽象类实现它。未经测试,但大致如下:

public interface IValidator 
{
    bool Validate(object input);
}

public abstract class Validator<T> : IValidator where T : IValidatable
{
    public abstract bool Validate(T input);

    public bool Validate (object input)
    {
        return Validate ((T) input);
    }
}

public static class ValidatorFactory
{
    public static IValidator GetValidator(ValidationType validationType)
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator();
        }
    }
}

然后这段代码:

IValidator myValidator = ValidatorFactory.GetValidator(ValidationType.AccountCreate); 

应该没问题。

【讨论】:

  • 看起来更好,但是编译器抱怨 Validator 没有实现 IValidator.Validate(object input)
  • 我用一个解决方案更新了答案,不幸的是,一旦你放弃在任何地方定义通用参数,那么你必须处理如果你被错误类型的参数调用会发生什么......我想您的解决方案将取决于您是否知道呼叫站点的验证类型。如果你这样做了,那么 Foo42 的解决方案会更好,因为它保留了强类型。如果你不这样做,那么你可能不得不与这样的解决方案妥协
【解决方案3】:

通常我会有一个接受Type 作为参数的工厂,以便工厂创建一个唯一的派生类型。但是因为您有多个验证器接受相同类型的对象进行验证(在这种情况下为IAccount),我认为您必须向您的工厂提供通用参数以及要创建的验证器类型,就像这样:

public static class ValidatorFactory
{
    public static Validator<T> GetValidator<T>(ValidationType validationType) 
        where T : IValidatable
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator() as Validator<T>;
                    // etc...
        }
    }
}

我试过打电话:

var value = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

现在这会将AccountCreateValidator 转换为正确的Validator&lt;IAccount&gt; 类型。

这并不完全理想,因为您现在需要知道您想要什么验证器以及它接受什么输入,但是如果由于我添加了显式转换,如果传递了错误的输入类型,您应该会收到编译错误,例如ValidatorFactory.GetValidator&lt;IUser&gt;(ValidationType.AccountCreate) 应该会失败。

编辑: 因为评论说这不会编译,所以我编辑了上面的代码 sn-p 以将转换为new AccountCreateValidator() as Validator&lt;T&gt;。我认为它可以以任何一种方式进行,但显然不是(虽然不知道为什么)。我已在 LINQPad 中验证了此功能,并且可以从验证器中返回结果。

我也相信我之前关于如果传入错误的泛型类型可能会出现编译错误的评论不再代表使用 as 关键字进行强制转换,如果它无法做到,则只会返回 null

【讨论】:

  • 谢谢,但它给了我以下错误:无法将类型“AccountCreateValidator”转换为“Validator
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多