使用反射访问已知类的某个未知字段
本文关键字:未知 字段 反射 访问 | 更新日期: 2023-09-27 18:18:25
假设我有以下类:
class Person
{
public FirstType firstField ; // has a getter-setter
public SecondType secondField; // has a getter-setter
// more fields of other types
}
我想构建一个函数,它接受Person
类作为其第一个参数,并接受第一个参数字段之一的类型作为其第二个参数-假设Person
的字段具有不同的类型。
我的目标是能够在第二个参数上使用反射,然后使用一些等效的点符号来访问传递的类。
public void SetPersonField(Person person, <should it be Type?> personFieldType)
{
// accessing person's correct field type
}
怎么做呢?
通过反射可以很容易地做到这一点:你可以获得一个类型的所有公共属性,找到给定类型的一个,并在传递的person
实例的上下文中设置其值。
我还建议使用泛型方法,以获得更好的类型安全性和更简洁的使用:
public static void SetPersonField<T>(Person person, T value)
{
typeof (Person)
.GetProperties()
.Single(p => p.PropertyType == typeof(T))
.SetValue(person, value);
}
// Usage (both are correct):
SetPersonField(person, FirstType.EnumValue);
SetPersonField<FirstType>(person, FirstType.EnumValue);
你甚至不需要指定泛型类型,因为它可以从输入参数中理解。
注意,如果没有这种类型的属性,或者如果有多个这种类型的属性,.Single
将抛出异常。如果不能保证存在性或唯一性,则需要在SetPersonField
方法中处理。
此外,要始终记住使用反射时的巨大性能下降。
更通用呢?
你可以实现一个更通用的方法,它将支持任何类型的属性和对象:
public static void SetField<TObject, TProperty>(TObject obj, TProperty value)
{
typeof(TObject)
.GetProperties()
.Single(p => p.PropertyType == typeof(TProperty))
.SetValue(obj, value);
}
var person = new Person();
SetField(person, 123); // finds the only int property and sets its value
SetField(person, FirstType.EnumValue); // find the only FirstType property
SetField<Person, DateTime?>(person, DateTime.Now); // finds the only DateTime? property
请注意,对于某些特定类型(例如,Nullable),您需要显式指定类型,因为无法从您的数据中理解。
我以前使用过这个类
Public Class PropertySetter
Public Shared Sub SetObjectProperty(ByRef obj As Object, propertyInfo As PropertyInfo, propertyValue As Object)
'if we do not need to convert propertyValue to certain type
If IsNothing(propertyValue) OrElse propertyInfo.PropertyType = propertyValue.GetType() Then
obj.GetType().GetProperty(propertyInfo.Name).SetValue(obj, propertyValue)
Else
'Convert.ChangeType does not handle conversion to nullable types
'if the property type is nullable, we need to get the underlying type of the property
Dim targetType = If(IsNullableType(propertyInfo.PropertyType), Nullable.GetUnderlyingType(propertyInfo.PropertyType), propertyInfo.PropertyType)
'convert Enum
If (propertyValue.GetType() = GetType(String)) Then
If propertyInfo.PropertyType.IsEnum OrElse (Not Nullable.GetUnderlyingType(propertyInfo.PropertyType) Is Nothing AndAlso Nullable.GetUnderlyingType(propertyInfo.PropertyType).IsEnum) Then
'if propertyValue is equal to enum value
Try
propertyValue = [Enum].Parse(targetType, propertyValue.ToString())
Catch ex As Exception
'if propertyValue is equal to enum description
Dim enumValues = targetType.GetFields()
For Each enumValue In enumValues
Dim descriptionAttribute = enumValue.GetCustomAttribute(Of DescriptionAttribute)()
If Not descriptionAttribute Is Nothing Then
If (descriptionAttribute.Description.Trim().ToUpper() = propertyValue.ToString().ToUpper()) Then
propertyValue = enumValue.GetValue(Nothing)
Exit For
End If
End If
Next
End Try
End If
End If
'set object property via reflection
obj.GetType().GetProperty(propertyInfo.Name).SetValue(obj, Convert.ChangeType(propertyValue, targetType, CultureInfo.InvariantCulture))
End If
End Sub
Private Shared Function IsNullableType(type As Type) As Boolean
Return type.IsGenericType AndAlso type.GetGenericTypeDefinition().Equals(GetType(Nullable(Of )))
End Function
End Class
注:如你所见,它是vb.net。你可以使用这个服务来获取c#代码
为将来参考,下面是如何创建Getter查询:
public static object GetPersonField(Person person, Type fieldType)
{
return typeof (Person)
.GetProperties()
.Single(p => p.PropertyType == fieldType)
.GetValue(person);
}