Short version

如何强制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> { }

这是否有可能,或者我应该使用完全不同的解决方案来实现这一点?

Long version

我试图将一些验证逻辑提取到基类中,但我不知道如何约束泛型类型,因此生成的基类是完全可靠的.

下面是一个没有基类的所有验证逻辑的示例.我使用FluentValidation来验证对象,并通过IDataErrorInfo接口公开验证结果,以便WPF UI可以使用它.

Original solution

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);
    }
}

Validation implementation separated into a base class

我想将验证逻辑和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; }
}

The problem

此解决方案的问题是,您可以编写以下无效代码:

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>
{
}

这是一种classic 模式,其中泛型参数被约束到派生类.

Csharp相关问答推荐

ASP.NET Core -是否可以对所有最小API端点应用过滤器?

如何从泛型方法返回一个可为空的T,其中T:notnull?

LINQ无法翻译SQLFunctions方法

为什么我的ASP.NET核心MVC应用程序要为HTML元素添加一些标识符?

如何注销Microsoft帐户?

使用C#中的SDK在Microsoft Graph API上使用SubscribedSkus的问题

UWP应用程序try 将打包的本机.exe文件加载为C#程序集

为基本审计设置Audit.EntityFramework.Core

正在寻找新的.NET8 Blazor Web应用程序.如何将.js添加到.razor页面?

在Windows Plesk上发布ASP.NET Core 7 Web API-错误:无法加载文件或程序集';Microsoft.Data.SqlClient';

错误CS1061';AuthenticationBuilder';不包含AddOpenIdConnect的定义

为什么我的伺服电机不动,下面的代码?

如何在.NET MAUI中最大化GraphicsView的大小?

在集成测试中可以在模拟InMemory数据库中设定数据种子

.NET8Blazor-为什么Rapzor渲染在for循环之后显示?

从MudAutoComplete打开对话框,列表仍然可见

C#中COM对象的实际地址

如何根据分割文本的块数来计算文本的大小?

无法将.Net Framework 4.8.1升级到.Net 7

在Swagger中显示自定义属性的属性名称