【问题标题】:C# Reflection with recursion带有递归的 C# 反射
【发布时间】:2013-01-18 23:11:11
【问题描述】:

我正在处理 Reflection ,但是在进行递归时我被卡住了。

代码:

public class User {
  public string Name;
  public int Number;
  public Address Address;    
}


public class Address {
 public string Street;
 public string State;
 public string Country;
}

现在我正在打印值。

 Type t = user.GetType();  
 PropertyInfo[] props = t.GetProperties(); 
 foreach (PropertyInfo prp in props)  
 {  
   if(!prp.GetType().IsPrimitive && prp.GetType().IsClass) 
   {
     // Get the values of the Inner Class.
     // i am stucked over here , can anyone help me with this.

           Type ty = prp.GetType();
           var prpI = ty.GetProperties();
           //var tp = ty.GetType().;
            foreach (var propertyInfo in prpI)
            {
            var value = propertyInfo.GetValue(prp);
            var stringValue = (value != null) ? value.ToString() : "";
            console.WriteLine(prp.GetType().Name + "." + propertyInfo.Name+" Value : " +stringValue);    
            }
   }
   else
   {    
     var value = prp.GetValue(user);   
     var stringValue = (value != null) ? value.ToString() : "";
     console.writeline(user.GetType().Name + "." + prp.Name+" Value : " +stringValue); 
   }
 }

我想知道如何确定属性是类还是原语。如果它是一个类,则进行递归。

【问题讨论】:

  • 你为什么不发布一些至少可以编译的东西?
  • 你确定反射是问题的解决方案吗?为什么要这样做?
  • 你为什么不试试 Automapper 它会做的工作。
  • 如果你想以这种方式工作,字典而不是类就可以了。
  • @IamStalker ,Automapper 肯定会工作,但我想自己编写这段代码。所以任何人都可以帮助我找出属性是原始的还是它的类。

标签: c# reflection recursion nested nested-class


【解决方案1】:

首先,如果您想访问一个类型的属性,请确保您使用的是具有属性的类型:

public class User {
  public string Name{get;set;}
  public int Number{get;set;}
  public Address Address{get;set;}    
}


public class Address {
 public string Street{get;set;}
 public string State{get;set;}
 public string Country{get;set;}
}

其次,prp.GetType() 将始终返回 PropertyInfo。您正在寻找prp.PropertyType,它将返回属性的类型。

另外,if(!prp.GetType().IsPrimitive && prp.GetType().IsClass) 不会按照您想要的方式工作,因为 String 例如是一个类,也不是一个原始的。最好使用prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary"。

最后但同样重要的是,要使用递归,您实际上必须将代码放入方法中。

这是一个完整的例子:

IEnumerable<string> GetPropertInfos(object o, string parent=null)
{
    Type t = o.GetType();  
    PropertyInfo[] props = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
    foreach (PropertyInfo prp in props)  
    {  
        if(prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
        {
            // fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
            foreach(var info in GetPropertInfos(prp.GetValue(o), t.Name))
                yield return info; 
        }
        else
        {    
            var value = prp.GetValue(o);   
            var stringValue = (value != null) ? value.ToString() : "";
            var info = t.Name + "." + prp.Name + ": " + stringValue;
            if (String.IsNullOrWhiteSpace(parent))
                yield return info; 
            else
                yield return parent + "." + info; 
        }
    }
}

这样使用:

var user = new User { Name = "Foo", Number = 19, Address = new Address{ Street="MyStreet", State="MyState",  Country="SomeCountry" }    };
foreach(var info in GetPropertInfos(user))
    Console.WriteLine(info);

它会输出

User.Name: Foo
User.Number: 19
User.Address.Street: MyStreet
User.Address.State: MyState
User.Address.Country: SomeCountry

【讨论】:

  • 非常感谢,输出完全符合我的需要,非常出色,从早上开始我就一直在摸不着头脑,只想说一声非常感谢。
  • @sloth:在递归类的情况下,这有可能导致堆栈溢出。即用户类有一个属性,它也是一个用户。一般来说,所有递归方法都应该有一些方法来防止无限递归的情况。 (就像使用 maxDepth 参数一样)
  • @galford13x 是的,但我认为更需要注意的问题是循环依赖,例如一个 User 实例引用了一个 Address 实例,而该实例又引用了相同的 User 实例。
  • @sloth:这就是我所说的递归类的意思。一个 User 类,它还包含一个 User 属性(或任何具有 User 属性的 Property)。我同意循环引用是更好的术语,因为结果会是无限递归。
  • 如果 User 类在不同的类库中,我看不出这段代码是如何工作的。 Module.ScopeName 将类似于 WhateverClassLibrary.dll
【解决方案2】:

首先,除非你真的需要,否则避免使用反射。它很慢,很乱,几乎无法调试(我喜欢它,但那是另一回事)

如果你想转储对象的全部内容,我建议将其转移到对象本身,它们应该知道它们的内部状态。你可以使用对象ToString的方法来写它们的内部状态,或者你可以定义一个显示内部状态的接口

interface IStateDisplay
{
   string GetInnerState();
}

并让你的对象实现它。那么显示属性的代码就是

Console.WriteLine(user.GetInnerState());

另一种选择是使用像 AutoMapper 这样的工具,它可以向您隐藏反射的复杂性和复杂性,并公开一个不错的 API 以供使用。

但是,如果您正在学习反射,打印复杂对象的状态是一个很好的练习。在这个方向上的一些指示:

public string Name;

不是属性,而是字段,所以type.GetProperties() 不会返回它。阅读 C# 属性是什么,以及如何使用和定义它们。一个最小的属性声明是

public string Name {get; set;}

此外,prp.GetType() 将返回 PropertyInfo 类型的类型信息,而不是它包含的属性类型。在这种情况下,您需要的是 prp.PropertyType 属性。

接下来,我认为Type.IsPrimitive 支票不是您想要的。该属性对于 Boolean、Byte、SByte、Int16、UInt16、Int32、UInt32、Int64、UInt64、IntPtr、UIntPtr、Char、Double 和 Single 类型返回 true,对于其他所有类型返回 false。最重要的是 typeof(string).IsPrimitive 返回 false。

同样,我认为Type.IsClass 支票也不是您想要的。当您使用它时,仅检查属性是值还是引用类型,并且由于值类型 (struct) 也可能非常复杂并且包含它们自己的属性和字段,因此检查不会感觉。

【讨论】:

  • 实现状态接口的概念是纯金。比一堆复杂且不需要的反射要好得多。我在我的一些视图模型上使用了状态接口,这些视图模型具有我需要在操作过滤器中访问的(公共)ID 属性。效果很棒。谢谢。
【解决方案3】:
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;


namespace Extensions
{
    public static class ObjectExtension
    {
        public static string ToStringProperties(this object o)
        {
            return o.ToStringProperties(0);
        }
    public static string ToStringProperties(this object o, int level)
    {
        StringBuilder sb = new StringBuilder();
        string spacer = new String(' ', 2 * level);
        if (level == 0) sb.Append(o.ToString());
        sb.Append(spacer);
        sb.Append("{\r\n");
        foreach (PropertyInfo pi in o.GetType().GetProperties())
        {
        if (pi.GetIndexParameters().Length == 0)
        {
            sb.Append(spacer);
            sb.Append("  ");
            sb.Append(pi.Name);
            sb.Append(" = ");

            object propValue = pi.GetValue(o, null);
            if (propValue == null)
            {
                sb.Append(" <null>");
            } else {
                if (IsMyOwnType(pi.PropertyType))
                {
                    sb.Append("\r\n");
                    sb.Append(((object)propValue).ToStringProperties(level + 1));
                } else{
                    sb.Append(propValue.ToString());
                }
            }
            sb.Append("\r\n");
        }
    }
    sb.Append(spacer);
    sb.Append("}\r\n");
    return sb.ToString();
}
    private static bool IsMyOwnType(Type t) 
{
    return (t.Assembly == Assembly.GetExecutingAssembly());
}
}
}

【讨论】:

    【解决方案4】:

    感谢@Sloth,您的代码非常有用。对于那些获得“对象引用未设置为对象实例”的人来说,这是一个轻微的修改。错误。它创建的对象实例为 null 并且还处理数组。需要做更多的工作来处理所有可能的集合类型,但这是一个开始。

    public static IEnumerable<string> GetPropertInfos(object o, string parent = null)
        {
    
    
            Type t = o.GetType();
            //   String namespaceValue = t.Namespace;
    
    
            PropertyInfo[] props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo prp in props)
            {
                if (prp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
                {
                    // fix me: you have to pass parent + "." + t.Name instead of t.Name if parent != null
                    object value = prp.GetValue(o);
                    if (value == null)
                    {
    
                        value =
                            Activator.CreateInstance(Type.GetType(
                                (prp.PropertyType).AssemblyQualifiedName.Replace("[]", "")));
                    }
    
                    var propertInfos = GetPropertInfos(value, t.Name);
                    foreach (var info in propertInfos)
                        yield return info;
                }
                else
                {
                    var type = GetTypeName(prp);
    
                    var info = t.Name + "." + prp.Name ;
                    if (String.IsNullOrWhiteSpace(parent))
                        yield return info;
                    else
                        yield return parent + "." + info;
                }
            }
        }
    

    【讨论】:

      【解决方案5】:

      您可以使用Type.IsValueType 属性。

      【讨论】:

        猜你喜欢
        • 2011-02-11
        • 1970-01-01
        • 1970-01-01
        • 2021-02-27
        • 2022-01-24
        • 2015-11-18
        • 1970-01-01
        • 1970-01-01
        • 2012-12-28
        相关资源
        最近更新 更多