Home > other >  Check specific values of generic properties
Check specific values of generic properties

Time:11-24

Well, I always can hardcode checking properties in order to solve my case but I want to do it using reflection.

My generic type:

public class AnalyzedParameter<T>
{
    public T Value { get; set; }

    public bool Valid { get; set; }
}

My class:

public class Foo
{
    public AnalyzedParameter<int> A { get; set; }

    public AnalyzedParameter<string> B { get; set; }
}

I need to check each property that has type AnalyzedParameter by checking Valid property inside.

So my method has to be like this:

public bool IsValid()
    {
        var props = GetType().GetProperties().Where(prop => prop.PropertyType == typeof(AnalyzedParameter<>));
        var valid = props.All(p => ((AnalyzedParameter<object>) p.GetValue(this)).Valid);

        return valid;
    }

But it does not work. Any ideas?

CodePudding user response:

The first part answers your question directly - how to make what you're doing work. The second hopefully changes the problem and makes it easier.

I separated out the part that checks all of these Valid properties into a separate class to make it easier for me to work with. (If this was only for Foo then you wouldn't need reflection. You already know what properties Foo has.

public static class Validations
{
    // x is a Foo or any other object that 
    // has properties of type AnalyzedProperty<T>
    public static bool IsValid(object x) 
    {
        var analyzedParameterProperties = x.GetType()
            .GetProperties().Where(prop =>
                prop.PropertyType.GetGenericTypeDefinition() == typeof(AnalyzedParameter<>));

        var isValid = analyzedParameterProperties.All(analyzedParameterProperty => 
            GetIsValidValue(x, analyzedParameterProperty));
        return isValid;
    }

    private static bool GetIsValidValue(object x, PropertyInfo analyzedParameterProperty)
    {
        var analyzedParameter = analyzedParameterProperty.GetValue(x);
        if (analyzedParameter == null) return false; // or true?
        var analyzedParameterIsValidProperty = analyzedParameter.GetType()
            .GetProperty("Valid", typeof(bool));
        return (bool)analyzedParameterIsValidProperty.GetValue(analyzedParameter);
    }
}

The IsValid method takes an object (like an instance of Foo) and retrieves all of the properties where the type matches the open generic type AnalyzedParameter<>.

The messy part is that you can't use that open generic type to read the Valid property on each AnalyzedParameter object.

So the second method - GetIsValidValue

  • Gets the value of the property - an AnalyzedParameter object
  • Finds a Valid property that returns bool
  • Reads that property and returns its value.

This would be much easier if AnalyzedProperty<T> implemented some interface that had the Valid property. In that case you could just cast each property value as that interface and read the property that way instead of using reflection to find the Valid property.

public class AnalyzedParameter<T> : IHasValidation
{
    public T Value { get; set; }

    public bool Valid { get; set; }
}

public interface IHasValidation
{
    public bool Valid { get; set; }
}

Now the rest of the code can be a little simpler:

public static bool IsValid(object x)
{
    var analyzedParameterProperties = x.GetType()
        .GetProperties().Where(prop =>
            typeof(IHasValidation).IsAssignableFrom(prop.PropertyType));

    var analyzedParameterValues = analyzedParameterProperties.Select(property =>
        property.GetValue(x)).Cast<IHasValidation>();

    // This assumes that if the property is null, it's not valid.
    // You could instead check for value is null or .Valid == true.
    var isValid = analyzedParameterValues.All(value => value?.Valid == true);
    return isValid;
}

CodePudding user response:

This doesn't work because the PropertyType of your instance variables is not AnalyzedParameter<> but instead AnalyzedParameter<int> or AnalyzedParameter<string>.

You will likely need to enumerate the types you wish to check for, and construct concrete types from them. For example, in some test code I put together:

public class AnalyzedParameter<T>
{
    public T Value { get; set; }
    public bool Valid { get; set; } = false;
}

public class Foo
{
    public AnalyzedParameter<int> A { get; set; }
    public AnalyzedParameter<string> B { get; set; }
    public bool IsValid()
    {
        var thisType = GetType();
        Log.Verbose($"Type: {thisType}");
        var thisProperties = thisType.GetProperties();
        Log.Verbose($"Properties: {thisProperties}");

        foreach (PropertyInfo pi in thisProperties)
        {
            Log.Verbose($"type:{pi.PropertyType}, name:{pi.Name}");
            if (pi.PropertyType == typeof(AnalyzedParameter<>)) Log.Verbose("This property is an AnalyzedParameter<>");
            if (pi.PropertyType == typeof(AnalyzedParameter<int>)) Log.Verbose("This property is an AnalyzedParameter<int>");
            if (pi.PropertyType == typeof(AnalyzedParameter<string>)) Log.Verbose("This property is an AnalyzedParameter<string>");
        }
    }
}
[10:25:50 VRB] Type: FooTest.Foo
[10:25:50 VRB] Properties: System.Reflection.PropertyInfo[]
[10:25:50 VRB] type:FooTest.AnalyzedParameter`1[System.Int32], name:A
[10:25:50 VRB] This property is an AnalyzedParameter<int>
[10:25:50 VRB] type:FooTest.AnalyzedParameter`1[System.String], name:B
[10:25:50 VRB] This property is an AnalyzedParameter<string>
  • Related