如何使用反射来读取具有相同泛型但具有不同泛型参数的属性?
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;
}
好吧,为了解决我的问题,我总是可以硬编码检查属性,但我想使用反射来做到这一点。
我的通用类型:
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
属性 returnsbool
- 读取 属性 和 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;
}