如何在不遍历属性的情况下将类的实例表示为字符串

本文关键字:实例 表示 字符串 情况下 遍历 属性 | 更新日期: 2023-09-27 18:24:11

出于调试目的,我需要打印某些类实例的所有公共字段/属性。我能自动做到吗?.NET 4.0中可能有新功能?例如,WPF DataGrid可以做类似的事情。

这可以手动引用这个问题,遍历类字段并打印它们,但如果我可以使用一些库方法而不是自己编码,我会很感兴趣吗?

即,我想替换这个代码:

        foreach (PropertyInfo prop in typeof(ServerInfo).GetProperties())
        {
            result += prop.Name + " = " + prop.GetValue(si, null) + "'n";
        }
        foreach (FieldInfo prop in typeof(ServerInfo).GetFields())
        {
            result += prop.Name + " = " + prop.GetValue(si) + "'n";
        }
        return result;

如何在不遍历属性的情况下将类的实例表示为字符串

我不知道您为什么要寻找更好的代码。你的代码只有几行,对我来说很好。

如果你想修改任何类型的代码,那么很简单:

string Format(object obj)
{
    Type type = obj.GetType();
    foreach (PropertyInfo prop in type.GetProperties())
    {
        result += prop.Name + " = " + prop.GetValue(obj, null) + "'n";
    }
    foreach (FieldInfo field in type.GetFields())
    {
        result += field.Name + " = " + field.GetValue(obj) + "'n";
    }
    return result;
}

在这段代码中,另一个可以做得更好的地方是效率:应该使用StringBuilder而不是串联字符串。

您可以覆盖自定义类的ToString方法,并使用如下所述的方法:https://stackoverflow.com/a/6745255/559144

或者创建自己的库/助手,将代码放在链接上,对于所有要使用的类,只需从ToString重写中调用助手方法即可。

不确定你所说的WPF Datagrid是什么意思。你确定你说的不是Visual Studio调试器数据集可视化工具吗?

引自Thomas:

User user = ...
foreach(PropertyInfo prop in typeof(User).GetProperties())
{
    Console.WriteLine("{0} = {1}", prop.Name, prop.GetValue(user, null));
}

如果您的目标是用WPF显示一个简单的属性列表,那么对对象使用带有ItemsSource"DataBound"的ListBox可能会有所帮助。为了从该对象中获得作为字符串的属性列表,请使用IValueConverter的一些实现,该实现读取最后几条注释中描述的属性,并将它们放入IEnumerable中。

在DataBinding中交换对象时,会立即显示新对象。

如果您真的对所有属性和字段都进行了设置,那么没有比您已经编写的代码更好的方法了。您可以编写一个扩展方法并将其合并到System命名空间中,这样它就会出现在任何类型的每个实例上;我也会认真考虑使用StringBuilderAppendLinestring.Format:

namespace System
{
  public static class MyExtensions
  {
    public static string Report<T>(this T instance)
    {
      StringBuilder sb = new StringBuilder();
      foreach (PropertyInfo prop in typeof(T).GetProperties())   
      {   
        sb.AppendLine(
          string.Format("{0} = {1}", prop.Name, prop.GetValue(si, null));   
      }   
      foreach (FieldInfo prop in typeof(T).GetFields())   
      {   
        sb.AppendLine(
          string.Format("{0} = {1}", prop.Name, prop.GetValue(si, null));   
      }   
      return sb.ToString(); 
    }
  }
}

一旦你写了这篇文章,你可以简单地做:

var instance = new ServerInfo();
var report = instance.Report();

这样做的问题是,它无法处理类型的最佳表示实际上只是"ToString"调用的情况,例如intstring(尤其是字符串;因为此代码只会输出Length成员)。

您提到了WPF数据网格——值得一提的是,它不直接使用反射,而是使用System.ComponentModel.TypeDescriptor类——它是可扩展的,并使用TypeConverter机制来支持到字符串的转换(比简单地依赖ToString要好得多)。

但是,它在生成的元数据中不包括字段;这显然是你想要的。

最后,我认为这种东西不应该在程序中大量使用;因为这种程度的细节很少真正重要。然后是你的报告应该深入到什么程度的问题;对于每个成员,您实际上是否应该不再使用Report(在这种情况下,您必须延迟绑定泛型,或者只编写它以使用object)?但是,您有一些讨厌的代码来检查应该降为或仅为ToString'd的类型。如果你开始这样做,你也需要注意不要通过交叉引用重复出现;否则,您的报告方法可能会锁定您的应用程序。

相反,我建议接口是最好的:

public interface IReportable
{
  void Report(StringBuilder target);
}

然后,接口的实现仅限于您真正需要报告的类型;您可以使用上面的扩展方法或类似的方法,作为大多数类型的默认实现。然而,至关重要的是,如果需要,一个类型可以提供自己的。

您可以为Type类编写扩展方法:

public static class TypeExtensions
{
    public static List<string> GetFieldsAndPropertiesAsString(this Type type)
    {
        return new List<string>(type.GetFieldsAsString().Union(type.GetPropertiesAsString()));
    }
    public static IEnumerable<string> GetFieldsAsString(this Type type)
    {
        // Modify BindingFlags to get what you want (instance only, private/public...)
        foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
        {
            yield return field.Name;
        }
    }
    public static IEnumerable<string> GetPropertiesAsString(this Type type)
    {
        // Modify BindingFlags to get what you want (instance only, private/public...)
        foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
        {
            yield return prop.Name;
        }
    }
}

然后使用进行调用

foreach (string zz in typeof(MyClass).GetFieldsAndPropertiesAsString())
{
    Console.WriteLine(zz);
}