是否可以将泛型参数类型约束为此?



短版

如何强制BaseClassTModel泛型参数与从它派生的类具有相同的类型?

public class BaseClass<TModel, TValidator> where TValidator : IValidator<TModel> { }
public class Person : BaseClass<Person, PersonValidator> { }

在这个例子中,我如何强制BaseClassTModelPerson类型而不是其他类型?

这是无效的语法,但它是我想象的:

public class BaseClass<TValidator> where TValidator : IValidator<this> { }
public class Person : BaseClass<PersonValidator> { }

这在某种程度上是可能的,还是我应该使用一个完全不同的解决方案来实现这一点?

长版

我试图将一些验证逻辑提取到基类中,但我不知道如何约束泛型类型,因此结果基类是完全万无一无的。

下面是一个没有基类的验证逻辑的示例。我正在使用FluentValidation来验证对象,并通过IDataErrorInfo接口暴露该验证结果,以便WPF UI可以使用它。

原始解决方案

public class User : IDataErrorInfo 
{
private readonly IValidator<Person> _validator = new();
public string Username { get; set; }
public string Password { get; set; }
private string ValidateAndGetErrorForProperty(string propertyName)
{
var result = _validator.Validate(this);
if (result.IsValid)
{
return string.Empty;
}
return result.Errors.FirstOrDefault(a => a.PropertyName == propertyName)?.ErrorMessage ?? string.Empty;
}
//IDataErrorInfo implementation
public string Error => string.Empty;
public string this[string columnName] => ValidateAndGetErrorForProperty(columnName);
}
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(a => a.Username)
.EmailAddress();
RuleFor(a => a.Password)
.MinimumLength(12);
}
}

验证实现分离为基类

我想把验证逻辑和IDataErrorInfo实现分离到一个基类中,这样这个样板文件就不必在每个模型类中重复。这是我的。

public abstract class ValidationBase<TModel, TValidator> : IDataErrorInfo where TValidator : IValidator<TModel>, new()
{
private readonly TValidator _validator;
public ValidationBase()
{
_validator = Activator.CreateInstance<TValidator>();
}
private string ValidateAndGetErrorForProperty(string propertyName)
{
//I have to check if this is of type TModel since the TModel isn't constraint to this
if (this is not TModel model)
{
throw new InvalidOperationException($"Instance is not of the supported type: {typeof(TModel)}. Type of {GetType()} found instead");
}
var result = _validator.Validate(model);
if (result.IsValid)
{
return string.Empty;
}
return result.Errors.FirstOrDefault(a => a.PropertyName == propertyName)?.ErrorMessage ?? string.Empty;
}
//IDataErrorInfo implementation
public string Error => string.Empty;
public string this[string columnName] => ValidateAndGetErrorForProperty(columnName);
}

我是这样使用它的:

public class User : ValidationBase<User, UserValidator>
{
public string Username { get; set; }
public string Password { get; set; }
}

这个解决方案的问题是你可以写这个无效的代码:

public class InvalidClass : ValidationBase<User, UserValidator>
{

}

这是你要找的吗?

public interface IValidator<TModel>
{
}
public class BaseClass<TModel, TValidator> 
where TModel : BaseClass<TModel, TValidator> 
where TValidator 
: IValidator<TModel> { }
// Only classes derived from BaseClass can be instantiated
public class Person 
: BaseClass<Person, PersonValidator> { }
public class PersonValidator 
: IValidator<Person>
{
}

这是一个经典的模式,其中泛型形参被约束为派生类。

最新更新