简体   繁体   English

C#委托与params

[英]C# delegate with params

I have a validate method that called some other implements: 我有一个调用其他工具的验证方法:

public ValidationResult Validate(Some arg) {
    var errors = new List<ValidationError>();

    validate1(arg, errors);
    if (errors.Count > 0) {
        return ValidationResult.Failed(errors);
    }

    validate2(arg, other, errors);
    if (errors.Count > 0) {
        return ValidationResult.Failed(errors);
    }

    validate3(arg, other2, errors);
    if (errors.Count > 0) {
        return ValidationResult.Failed(errors);
    }

    return ValidationResult.Succeess();
}

I want some way to make the code like below, use for loop to invoke each validator: 我想要一些方法来制作如下代码,使用for循环来调用每个验证器:

public ValidationResult Validate(Some arg) {
    var errors = new List<ValidationError>();

    var validators = new [] {
        validate1(arg, errors),
        validate2(arg, other, errors),
        validate3(arg, other2, errors)
    };

    foreach (var validator in validators) {
        validator.invoke();
        if (errors.Count > 0) {
            return ValidationResult.Failed(errors);
        }
    }

    return ValidationResult.Success();
}

How can I do it? 我该怎么做?

You can try this 你可以试试这个

var validators = new Action[]  {
    ()=>validate1(arg, errors),
    ()=>validate2(arg, other, errors),
    ()=>validate3(arg, other2, errors)
};

foreach (var v in  validators)
    v();

You could define a common interface for the validators and implement a class for each usecase. 您可以为验证器定义公共接口,并为每个用例实现一个类。

public interface IValidator {
    ValidationResult Invoke();
}

public class Validator1 : IValidator {
    private string _arg;
    private List<ValidationError> _errors;

    Validator1(string arg, List<ValidationError> errors) {
        _arg = arg; 
       _errors = errors
    }

    public ValidationResult Validate() {
        if (_errors.Count > 0) {
            return ValidationResult.Failed(_errors);
        }
        return ValidationResult.Success();
    }
}

Then you can work with a list of IValidator instances. 然后,您可以使用IValidator实例列表。

public ValidationResult Validate(Some arg) {
    var errors = new List<ValidationError>();

    var validators = new IValidator[] {
        new Validator1(arg, errors),
        new Validator2(arg, other, errors),
        new Validator3(arg, other2, errors)
    };

    foreach (var validator in validators) {
        var result = validator.Invoke();
        if (result != ValidationResult.Success()) {
            return result;
        }
    }

    return ValidationResult.Success();
}

Well, I consider implementing validation in Fluent-like style: 好吧,我考虑以类似Fluent的风格实现验证:

public interface IValidator<T>
{
    IEnumerable<ValidationError> Validate(T obj);
    IEnumerable<ValidationError> ValidateAll(IEnumerable<T> obj);
}


public class SomeTypeValidator : IValidator<SomeType>
{
    private readonly IValidator<SomeNestedType> _validator1;
    public SomeTypeValidator(IValidator<SomeNestedType> validator1)
    {
        _validator1 = validator1;
    }

    public IEnumerable<ValidationError> Validate(SomeType obj)
    {
        yield return Error("My first error");
        foreach(var e in _validator1.Validate(obj.val1))
        {
             yield return e;
        }
        /*whatever you desire goes here*/
    }

    public IEnumerable<ValidationError> ValidateAll(IEnumerable<SomeType> objs)
    {
        return objs.SelectMany(Validate);
    }
}

Then some useful extension: 然后一些有用的扩展:

public static void ThrowIfInvalid(this IEnumerable<ValidationError> errors)
{
    if(errors == null)
       return;
    var e = errors.ToList();
    if(e.Any())
    {
        throw new Exception(\*use 'e' here to form exception*\);
    }
}

Then somewhere in code I call it like this: 然后代码中的某处我称之为:

_validator.Validate(new SomeType()).ThrowIfInvalid();

This way you will free yourself from those lists/packs of errors all over the place and just redirect validation error streams to whatever other validator you desire. 通过这种方式,您可以将自己从这些错误列表/包中解脱出来,并将验证错误流重定向到您想要的任何其他验证器。 Also you always can stop validation at some point, by calling yield break and will be able to create ansamble from them. 此外,您始终可以通过调用yield break来停止验证,并且可以从它们创建ansamble。

Thanks @tym32167 ! 谢谢@ tym32167!

And I have some additional about async: 我还有一些关于异步的补充:

        var validations = new Func<Task>[]
        {
            async () => await ValidateAsync(arg, other, errors)
        };

        foreach (var validation in validations)
        {
            await validation();
            if (errors.Count > 0)
            {
                return ValidationResult.Failed(errors);
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM