C# how to shorten multiple If expressions

Viewed 587

I have multiple validation boolean properties which can be required or not. If they are required they need to be checked for validation purposes. Therefore I had to build multiple if statements to handle each property. My question is if is there a better way to maintain this instead of having to write if for every new property.

public class ProductionNavbarViewModel
{
    public bool IsProductionActive { get; set; }
    public bool ValidatedComponents { get; set; }
    public bool ValidatedGeometries { get; set; }
    public bool ValidatedPokayokes { get; set; }
    public bool ValidatedTechnicalFile { get; set; }
    public bool ValidatedStandardOperationSheet { get; set; }
    public bool ValidatedOperationMethod { get; set; }
    public bool IsComponentsRequired { get; set; }
    public bool IsGeometriesRequired { get; set; }
    public bool IsPokayokesRequired { get; set; }
    public bool IsTechnicalFileRequired { get; set; }
    public bool IsStandardOperationSheetRequired { get; set; }
    public bool IsOperationMethodRequired { get; set; }


    public bool IsProductionReadyToStart()
    {
        if (IsComponentsRequired)
        {
            return ValidatedComponents;
        }

        if (IsComponentsRequired && IsGeometriesRequired)
        {
            return ValidatedComponents && ValidatedGeometries;
        }

        if (IsComponentsRequired && IsGeometriesRequired && IsPokayokesRequired)
        {
            return ValidatedComponents && ValidatedGeometries && ValidatedPokayokes;
        }

        if (IsComponentsRequired && IsGeometriesRequired && IsPokayokesRequired && IsTechnicalFileRequired)
        {
            return ValidatedComponents && ValidatedGeometries && ValidatedPokayokes && ValidatedTechnicalFile;
        }

        if (IsComponentsRequired && IsGeometriesRequired && IsPokayokesRequired && IsTechnicalFileRequired && ValidatedStandardOperationSheet)
        {
            return ValidatedComponents && ValidatedGeometries && ValidatedPokayokes && ValidatedTechnicalFile && ValidatedStandardOperationSheet;
        }

        if (IsComponentsRequired && IsGeometriesRequired && IsPokayokesRequired && IsTechnicalFileRequired && IsStandardOperationSheetRequired && IsOperationMethodRequired)
        {
            return ValidatedComponents && ValidatedGeometries && ValidatedPokayokes && ValidatedTechnicalFile && ValidatedStandardOperationSheet && ValidatedOperationMethod;
        }

        return false;
    }
}

EDIT

There was a problem while making this code. The intended is to validate all options, they must be necessary, it cannot return if just one property meets the condition.

Thanks everyone, I will try some of the suggested approaches on the comments and I'll post the results after

UPDATE

I have come with a short version for now and more readable based on everyones comments until I can try every approach. Edited to combine all expressions as per @Alexander Powolozki answer.

    public bool IsProductionReadyToStart()
    {
        bool isValid = true;

        isValid &= !IsComponentsRequired || ValidatedComponents;
        isValid &= !IsGeometriesRequired || ValidatedGeometries;
        isValid &= !IsPokayokesRequired || ValidatedComponents;
        isValid &= !IsTechnicalFileRequired || ValidatedTechnicalFile;
        isValid &= !IsStandardOperationSheetRequired || ValidatedStandardOperationSheet;
        isValid &= !IsOperationMethodRequired || ValidatedOperationMethod;            

        return isValid;
    }
6 Answers

The correct implementation of the method should be something like following:

public bool IsProductionReadyToStart()
{
    bool isValid = true;

    isValid &= !IsComponentsRequired || ValidatedComponents;
    isValid &= !IsGeometriesRequired || ValidatedGeometries;
    isValid &= !IsPokayokesRequired || ValidatedPokayokes;
    isValid &= !IsTechnicalFileRequired || ValidatedTechnicalFile;
    isValid &= !IsStandardOperationSheetRequired || ValidatedStandardOperationSheet;
    isValid &= !IsOperationMethodRequired || ValidatedOperationMethod;            

    return isValid;
}

when not uing &= then you erase all previous results you checked instead of combine them.

It looks like a collection

public class Validation
{
    public bool Required { get; set; }
    public bool IsValid { get; set; }
}

var validations = new[]
{
    new Validation { Required = true, IsValid = true },
    new Validation { Required = false, IsValid = true },
    new Validation { Required = true, IsValid = false },
};

// return true only when all required validations are valid
public bool IsProductionReadyToStart()
{
    return _validations.Where(v => v.Required).All(v => v.IsValid);
}

I'd go with:

if (IsComponentsRequired && !ValidateComponents) return false;
if (IsGeometriesRequired && !ValidatedGeometries) return false;
...
return true;

This reads more like a checklist.

You may accumulate your conditions into ValueTuple collection, then check them all together

var conditions = new[]
{
    (IsComponentsRequired, ValidatedComponents),
    (IsGeometriesRequired, ValidatedGeometries),
    (IsPokayokesRequired, ValidatedPokayokes)
};

return conditions.Where(c => c.Item1).All(c => c.Item2);

You can also use named tuple syntax for readability

var conditions = new (bool isRequired, bool validated)[]
{
    (IsComponentsRequired, ValidatedComponents),
    (IsGeometriesRequired, ValidatedGeometries),
    (IsPokayokesRequired, ValidatedPokayokes)
};

return conditions.Where(c => c.isRequired).All(c => c.validated);

Something I like to use is &=, it looks pretty clean but the downside is not everyone is familiar with this approach.


bool result = true;

result &= !IsComponentsRequired || IsComponentsRequired && ValidatedComponents;
result &= !IsGeometriesRequired|| IsGeometriesRequired && ValidatedGeometries;
//...etc
return result;

This code would function the same as:

result = result && (!IsComponentsRequired || IsComponentsRequired && ValidatedComponents);
//...etc

But I think it looks cleaner with the bitwise operation.

I think what you really want is this:

return 
    (IsComponentsRequired && ValidatedComponents || IsComponentsRequired == false) &&
    (IsGeometriesRequired && ValidatedGeometries) || IsGeometriesRequired == false) &&
    (IsPokayokesRequired && ValidatedPokayokes || IsPokayokesRequired == false ) &&
    (IsTechnicalFileRequired && ValidatedTechnicalFile ||  IsTechnicalFileRequired == false) &&
    (IsStandardOperationSheetRequired && ValidatedStandardOperationSheet || IsTechnicalFileRequired == false) &&
    (IsOperationMethodRequired && ValidatedOperationMethod || IsOperationMethodRequired == false )

This can be implemented using reflection, so you don't need to change the code when adding more properties:

public class ProductionNavbarViewModel
{
    public bool IsProductionActive { get; set; }
    public bool ValidatedComponents { get; set; }
    public bool ValidatedGeometries { get; set; }
    public bool ValidatedPokayokes { get; set; }
    public bool ValidatedTechnicalFile { get; set; }
    public bool ValidatedStandardOperationSheet { get; set; }
    public bool ValidatedOperationMethod { get; set; }
    public bool IsComponentsRequired { get; set; }
    public bool IsGeometriesRequired { get; set; }
    public bool IsPokayokesRequired { get; set; }
    public bool IsTechnicalFileRequired { get; set; }
    public bool IsStandardOperationSheetRequired { get; set; }
    public bool IsOperationMethodRequired { get; set; }


    public bool IsProductionReadyToStart()
    {
        var validatedProps = this.GetType().GetProperties().Where(x => x.Name.StartsWith("Validated"));
        foreach (var validatedProp in validatedProps)
        {
            var concept = validatedProp.Name.Substring(9);
            var isRequiredProp = this.GetType().GetProperty("Is" + concept + "Required");
            var isRequired = (bool)isRequiredProp.GetValue(this);
            if (isRequired)
            {
                var isValid = (bool)validatedProp.GetValue(this);
                if (isValid == false) return false;
            }
        }
        return true;
    }
}
Related