下面是我的代码:
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
//some code
}
var props = type.GetProperties();
foreach (PropertyInfo prop in props)
{
//exact same code
}
我知道我可以创建一个函数,我可以调用两次,但我想做的(如果可能的话)是一个单一的foreach
。类似这样的代码(是的,代码不能工作。如果它有效,我就不会问这个问题了!):
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
var props = type.GetProperties();
foreach (PropertyInfo prop in fields && PropertyInfo prop in props)
{
//some code
}
我真的觉得有一种方法,即使我知道我的解决方案远非可编译:(
谢谢你的帮助!
如果您可以使用MemberInfo类(它是FieldInfo和PropertyInfo的基础)公开的属性,那么您可以执行以下操作:
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Cast<MemberInfo>();
var props = type.GetProperties().Cast<MemberInfo>();
var fieldsAndProperties = fields.Union(props);
foreach (MemberInfo mi in fieldsAndProperties)
{
...
}
如果MemberInfo
暴露的功能在循环中对您足够,那么您可以将字段和属性连接到同一个数组或其他可枚举对象上,并且只迭代一次。
这是可能的,因为FieldInfo
和PropertyInfo
都继承自MemberInfo
类。
示例代码:
var fields = typeof(DateTime).GetFields(
BindingFlags.Public | BindingFlags.Instance);
var properties = typeof(DateTime).GetProperties(
BindingFlags.Public | BindingFlags.Instance);
var all = fields.Cast<MemberInfo>().Concat(properties.Cast<MemberInfo>());
foreach (MemberInfo mi in all)
{
//some code
}
将它们放入一个循环:
foreach (MemberInfo member in fields.Cast<MemberInfo>().Concat(props))
{
//I'm 100% sure this isn't what you want
//because you need to set value here
//in this case, you still need to check if member is a FieldInfo/PropertyInfo
//and then convert it before you set the values
}
实际上,在这里使用两个循环可能更好。首先,代码看起来更清晰,因为它们在做不同的事情,一个设置字段的值,另一个设置属性。其次,寄存器缓存可能有助于使您的程序比一个循环快一点。
嗯....
这种迭代是丢失字段和属性的顺序。例如
class Test
{
public String tests { get; set; }
public int testi;
}
如果GetFields If get first -我们得到"testi"作为第一个成员,通过getproperties我们得到"tests"。Union可能会合并它们,但不保留顺序。
是否有办法获取字段和属性并保持它们的顺序?
我建议这样遍历对象并记录对象信息。
public static string ObjectToString(object obj)
{
var sb = new StringBuilder();
try
{
sb.AppendLine(obj.GetType().Name);
foreach (var prop in obj.GetType().GetProperties())
{
sb.AppendLine(string.Format("{0} Property Name: {1}; Value: [{2}]; Type: {3}", "--->", prop.Name, prop.GetValue(obj, null)?.ToString(), prop.PropertyType?.FullName));
}
foreach (var fld in obj.GetType().GetFields())
{
if (!fld.FieldType.Namespace.Equals("System", StringComparison.InvariantCultureIgnoreCase) && fld.GetValue(obj) != null)
{
ObjectToString(fld.GetValue(obj), sb);
}
else
{
sb.AppendLine(string.Format("{0} Field Name: {1}; Value: [{2}]; Type: {3}", "--->", fld.Name, fld.GetValue(obj)?.ToString(), fld.FieldType?.FullName));
}
}
}
catch (Exception ex)
{
sb.AppendLine("---> Exception in ObjectToString: " + ex.Message);
}
return sb.ToString();
}
public static string ObjectToString(object obj, StringBuilder sb, int depth = 1)
{
try
{
sb.AppendLine(string.Format("{0}{1} {2}", (depth==2?"----":""),"--->", obj.GetType().Name));
foreach (var prop in obj.GetType().GetProperties())
{
sb.AppendLine(string.Format("{0} Property Name: {1}; Value: [{2}]; Type: {3}", "------->", prop.Name, prop.GetValue(obj, null)?.ToString(), prop.PropertyType?.FullName));
}
foreach (var fld in obj.GetType().GetFields())
{
//we want to avoid stake overflow and go only one more depth
if (!fld.FieldType.Namespace.Equals("System", StringComparison.InvariantCultureIgnoreCase) && fld.GetValue(obj) != null && depth < 2)
{
ObjectToString(fld.GetValue(obj), sb, 2);
}
else
{
sb.AppendLine(string.Format("{0} Field Name: {1}; Value: [{2}]; Type: {3}", "------->", fld.Name, fld.GetValue(obj)?.ToString(), fld.FieldType?.FullName));
}
}
}
catch (Exception ex)
{
sb.AppendLine("-------> Exception in ObjectToString: depth(" + depth + ") " + ex.Message);
}
return sb.ToString();
}
使用反射访问成员属性的一个问题是速度很慢。有一个小的库,用一些动态生成的访问器代码,FastMember,可以很好地快速完成这项工作。要用它迭代所有公共字段和属性,您需要运行这样的简单循环:
var ta = TypeAccessor.Create(typeof(MyClass));
foreach (var prop in ta.GetMembers())
{
Console.WriteLine(prop.Name);
}
当你需要设置任何对象实例的字段值或属性值时,可以这样做:
var instance = new MyClass();
ta[instance, prop.Name] = "myvalue";