【问题标题】:Covariance in generic parameter and convention based on parameter type基于参数类型的泛型参数和约定中的协方差
【发布时间】:2018-09-14 11:08:03
【问题描述】:

我真的很难为 FluentValidator 创建基于接口/约定的规则。它有以下类

   abstract class AbstractValidator<T>
    {

    IRuleBuilderInitial<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression)
    ...
    }


public interface IWithPropertyA
{
  string PropertyA{get;set;}
}

public interface IWithPropertyB
{
 string PropertyB{get;set;}
}

public class Handler1Data: IWithPropertyA
{
 public string PropertyA {get;set;}
}
public class Handler2Data: IWithPropertyA, IWithPropertyB
{
 public string PropertyA {get;set;}
 public string PropertyB {get;set;}
}

public class Handler1 : AbstractValidator<Handler1Data> {}
public class Handler2 : AbstractValidator<Handler2Data> {}

我正在尝试创建扩展方法,该方法基本上会检查泛型参数是否实现特定接口,然后向其添加规则:

public static void ValidateAll<T>(this AbstractValidator<T> validator)
        {

           (validator as AbstractValidator<IWithPropertyA>)?.RuleFor(x => x.PropertyA).NotEmpty().WithMessage("PropertyA Missing");
           (validator as AbstractValidator<IWithPropertyB>)?.RuleFor(x => x.PropertyB).NotEmpty().WithMessage("PropertyB Missing");
        }

这里的问题显然是 AbstractValidator 不是协变的,因此验证器既不能转换为 AbstractValidator&lt;PropertyA&gt; 也不能转换为 AbstractValidator&lt;PropertyB&gt;。我尝试创建自己的 Base Validator,如下所示,然后基于此创建扩展方法,但我做不到。

public interface IMyValidator<in T>
{
   void AddMyRule<TProperty>(Expression<Func<T, TProperty>> expression) //it doesn't work because Expression<Func<T,Property> cannont be covariant
}

public abstract class MyBaseValidator<T>: AbstractValidator<T> ,IMyValidator<T>
{
   void AddMyRule<TProperty>(Expression<Func<T, TProperty>> expression)
}

每个 Handler 都会像这样调用方法:

public class Handler1 : AbstractValidator<Handler1Data> {
  this.ValidateAll();
}

【问题讨论】:

    标签: c# covariance contravariance


    【解决方案1】:

    你不是要写吗:

    (validator as AbstractValidator<IWithPropertyA>)
        ?.RuleFor(x => x.PropertyA)
         .NotEmpty()
         .WithMessage("Property1 Missing");
    

    因为Property1 没有在任何地方定义。

    【讨论】:

      【解决方案2】:

      我找到了一种避免使用 Expression 的解决方法,这显然是这里的问题。它的缺点是我们丢失了属性名称,我们必须手动配置消息。

       public interface IMyValidator<out T>
          {
              void AddMyRule<TProperty>(Func<T, TProperty> expression, string message);
          }
      
          public abstract class MyBaseValidator<T> : AbstractValidator<T>, IMyValidator<T>
              {
                  public void AddMyRule<TProperty>(Func<T, TProperty> expression, string message)
                  {
                      var exp = FuncToExpression(expression);
                      RuleFor(exp).NotEmpty().WithMessage(message);
                  }
      
                  private static Expression<Func<T, P>> FuncToExpression<T, P>(Func<T, P> f) => x => f(x);
              }
      
      public static class Ext
          {
              public static void ValidateAll<T>(this AbstractValidator<T> validator)
              {
                  (validator as IMyValidator<IWithPropertyA>)?.AddMyRule(x => x.PropertyA, "PropA Cant be empty");
                  (validator as IMyValidator<IWithPropertyB>)?.AddMyRule(x => x.PropertyB, "PropB Cant be empty");
              }
          }
      
       public class Handler1 : MyBaseValidator<Handler1Data>
          {
              public Handler1()
              {
                  this.ValidateAll();
              }
          }
          public class Handler2 : MyBaseValidator<Handler2Data> { }
      

      【讨论】:

        猜你喜欢
        • 2013-01-11
        • 1970-01-01
        • 2020-07-04
        • 2019-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-04
        相关资源
        最近更新 更多