我希望有一个类似于这样的东西可以用在我正在处理的项目中,并且我想要一个更好的通用方法来处理它。
我尝试了很多方法,但最终我选择了创建一个通用函数来获取任何类型的成员,然后只需为属性和字段调用它来获取值成员。
获取成员
public static MemberInfo[] GetValueMembers(this Type t, BindingFlags bindings)
{
MemberInfo[] result = t.GetMembersSlow(bindings, MemberTypes.Field | MemberTypes.Property);
return result;
}
public static MemberInfo[] GetMemberSlow(
this Type t, BindingFlags bindings, MemberTypes types
){
MemberInfo[] result = t.FindMembers(types, bindings, (o, p) => true, null);
return result;
}
这种方法的性能还不错,虽然不是最好的,但比我尝试过的其他方法要好。使用
IEnumerable<MemberInfo>.Concat()
将
GetProperties()
和
GetMethods()
的结果强制转换的方法相对较慢。
但是,通过执行 GetProperties()
和 GetFields()
并使用 Array.Copy
,我得到了非常好的性能,因此我决定编写一个基于您请求的 Type 上所有 GetX()
类型的内容,并将所有结果一起 Array.Copy()
的程序。
public static MemberInfo[] GetMembers(this Type t, BindingFlags bindings, MemberTypes types)
{
List<MemberInfo[]> resultArrays = new List<MemberInfo[]>();
MemberTypes[] memberTypes = types.Split();
MemberInfo[] buffer = null;
int totalLength = 0;
foreach (MemberTypes memberType in memberTypes)
{
switch (memberType)
{
case MemberTypes.Constructor:
buffer = t.GetConstructors(bindings);
break;
case MemberTypes.Event:
buffer = t.GetEvents(bindings);
break;
case MemberTypes.Field:
buffer = t.GetFields(bindings);
break;
case MemberTypes.Method:
buffer = t.GetMethods(bindings);
break;
case MemberTypes.Property:
buffer = t.GetProperties(bindings);
break;
default:
break;
}
if (buffer != null && buffer.Length > 0)
{
resultArrays.Add(buffer);
totalLength += buffer.Length;
buffer = null;
}
}
MemberInfo[] result = new MemberInfo[totalLength];
int currentIndex = 0;
foreach (MemberInfo[] resultArray in resultArrays)
{
if (resultArray.Length == 0)
continue;
Array.Copy(resultArray, 0, result, currentIndex, resultArray.Length);
currentIndex += resultArray.Length;
}
return result;
}
这里有一个稍微隐藏的东西,在 types.Split()
的调用中,我写了一个函数,它接收一个 flags 枚举并返回包含其中所有单独的标志的数组,以便于循环使用。有关 Split()
代码,请参见https://dev59.com/snNA5IYBdhLWcg3wPbOe#72251903。
哦,这很重要,一开始我没有理解: MemberInfo
是一个抽象基本类型,所有的 XInfo
类型都是从它派生出来的,并且很容易被转换为和从中转换。所以,要将 ConstructorInfo[]
更改为 MemberInfo[]
,只需将其分配给一个 MemberInfo[]
变量,或者使用 (MemberInfo[])
或 X as MemberInfo[]
进行转换等。
根据传入的参数不同,此函数将从 Type 中获取与传入绑定/成员类型相关的所有相关的 MemberInfo
数组,从特定函数中单独获取它们,然后将它们全部复制到一个大的 MemberInfo[]
中。正如我提到的,我在 Properties+Fields 上表现优异,但我担心由于这么多 overhead 会减慢速度。
结果我担心是没有必要的,它比我尝试过的任何其他方法都表现出色,差距还不小。这段代码已经经过了边缘测试,并正常工作。我测试了 Forms.GetType()
上剩下的两个 GetMembers()
,并且与 Type.FindMembers()
返回的内容一致。
使用成员
最后,我编写了一些帮助实际利用 Properties+Fields 数据的辅助程序。由于 MemberInfo
是抽象的,因此没有方便的方法从成员实例中获取值。
当您需要从 FieldInfo
或 PropertyInfo
中获取信息或其实例的值时,只需创建一个 MemberFactory,将字段或属性信息作为 MemberInfo
传递即可。
如果您需要成员类型,请调用 GetMemberSystemType()
,该函数将返回成员的 System.Type
。
注意: 这可能远远不能处理所有情况,但对于基本成员它是有效的。
public struct MemberFactory
{
public readonly FieldInfo fieldInfo;
public readonly PropertyInfo propertyInfo;
private MemberFactory(MemberInfo memberInfo)
{
fieldInfo = memberInfo as FieldInfo;
propertyInfo = memberInfo as PropertyInfo;
if (fieldInfo == null && propertyInfo == null)
throw new ArgumentException("memberInfo must derive from FieldInfo or PropertyInfo", memberInfo.Name);
}
public Type GetMemberSystemType()
{
Type result = null;
if (IsProperty)
result = propertyInfo.PropertyType;
else if (IsField)
result = fieldInfo.FieldType;
return result;
}
public bool IsProperty { get => propertyInfo != null; }
public bool IsField { get => fieldInfo != null; }
public Member MakeMember(object instance)
{
return new Member(instance, this);
}
public static MemberFactory Create(MemberInfo memberInfo)
{
return new MemberFactory(memberInfo);
}
}
然后,当您有一个实际的实例需要处理时,只需调用MakeMember()
并使用该实例获取一个Member
对象来操作该实例。
由于我们需要检查属性和字段,因此在实现中,我只使用了空值条件/空值合并来同时检查它们。我没有测试在MemberFactory
上仅检查IsMethod()
/IsProperty()
是否会对性能产生很大影响,但我认为这样做没问题。
最后,还有一些扩展函数,以便如果您拥有MemberInfo
和一个实例,您不必创建MemberFactory
/Member
即可使用它们,以防您只需要快速访问一个值而不需要进行大量设置。
public class Member
{
object instance;
MemberFactory factory;
public Member(object instance, MemberFactory factory)
{
this.instance = instance;
this.factory = factory;
}
public object GetValue()
{
return
factory.fieldInfo?.GetValue(instance)
?? factory.propertyInfo?.GetValue(instance);
}
public void SetValue(object value)
{
factory.fieldInfo?.SetValue(instance, value);
factory.propertyInfo?.SetValue(instance, value);
}
}
public static void SetValue(this MemberInfo memberInfo, object instance, object value)
{
MemberFactory.Create(memberInfo).MakeMember(instance).SetValue(value);
}
public static object GetValue(this MemberInfo memberInfo, object instance)
{
return MemberFactory.Create(memberInfo).MakeMember(instance).GetValue();
}
关于这个问题就是这样了。我需要它来做其他事情,当我在寻求帮助时,这是我遇到的页面之一,所以我记下来了,并在有一些可靠的半测试想法后回来。
启发我进行此项工作的核心思想基本上就是在Member.SetValue()
调用中。