PM Extra
PM Extra

Reputation: 435

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:

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?

Upvotes: 2

Views: 340

Answers (4)

PM Extra
PM Extra

Reputation: 435

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

Upvotes: 0

Georg Patscheider
Georg Patscheider

Reputation: 9463

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.

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

Upvotes: 1

eocron
eocron

Reputation: 7546

Well, I consider implementing validation in Fluent-like style:

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.

Upvotes: 0

tym32167
tym32167

Reputation: 4891

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

Upvotes: 7

Related Questions