簡體   English   中英

是否可以將泛型參數類型限制於此?

[英]Is it possible to constrain a generic parameter type to this?

精簡版

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

這是一個經典模式,其中泛型參數被限制在派生類中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM