如何使用反射来读取具有相同泛型但具有不同泛型参数的属性?

How do I use reflection to read properties with the same generic type but different generic arguments?

好吧,为了解决我的问题,我总是可以硬编码检查属性,但我想使用反射来做到这一点。

我的通用类型:

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

    public bool Valid { get; set; }
}

我的class:

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

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

我需要通过检查内部的 Valid 属性 来检查类型为 AnalyzedParameter 的每个 属性。

所以我的方法必须是这样的:

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

但它不起作用。有什么想法吗?

这不起作用,因为实例变量的 PropertyType 不是 AnalyzedParameter<>,而是 AnalyzedParameter<int>AnalyzedParameter<string>

您可能需要枚举要检查的类型,并从中构造具体类型。例如,在我放在一起的一些测试代码中:

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>

第一部分直接回答了您的问题 - 如何让您正在做的事情发挥作用。第二个希望能改变问题并使其变得更容易。

我将检查所有这些 Valid 属性的部分分离到一个单独的 class 中,以便于我使用。 (如果这仅适用于 Foo,那么您就不需要反射。您已经知道 Foo 具有哪些属性。

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

IsValid 方法获取一个对象(如 Foo 的实例)并检索类型与开放通用类型匹配的所有属性 AnalyzedParameter<>.

混乱的部分是您不能使用开放泛型类型来读取每个 AnalyzedParameter 对象上的 Valid 属性。

所以第二种方法 - GetIsValidValue

  • 获取 属性 的值 - AnalyzedParameter 对象
  • 找到 Valid 属性 returns bool
  • 读取 属性 和 returns 它的值。

如果 AnalyzedProperty<T> 实现了一些具有 Valid 属性 的接口,这会容易得多。在那种情况下,您可以将每个 属性 值转换为该接口并以这种方式读取 属性 而不是使用反射来查找 Valid 属性.

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

    public bool Valid { get; set; }
}

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

现在剩下的代码可以简单一点:

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