循环一个类型的GetFields和GetProperties



下面是我的代码:

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暴露的功能在循环中对您足够,那么您可以将字段和属性连接到同一个数组或其他可枚举对象上,并且只迭代一次。

这是可能的,因为FieldInfoPropertyInfo都继承自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";

相关内容

  • 没有找到相关文章

最新更新