【问题标题】:Dynamic override of ToString() using Reflection使用反射动态覆盖 ToString()
【发布时间】:2012-02-15 18:40:40
【问题描述】:

我通常重写 ToString() 方法以输出属性名称和与它们关联的值。我有点厌倦了手工写这些,所以我正在寻找一个动态的解决方案。

主要:

TestingClass tc = new TestingClass()
{
    Prop1 = "blah1",
    Prop2 = "blah2"
};
Console.WriteLine(tc.ToString());
Console.ReadLine();

测试类:

public class TestingClass
{
    public string Prop1 { get; set; }//properties
    public string Prop2 { get; set; }
    public void Method1(string a) { }//method
    public TestingClass() { }//const
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (Type type in System.Reflection.Assembly.GetExecutingAssembly().GetTypes())
        {
            foreach (System.Reflection.PropertyInfo property in type.GetProperties())
            {
                sb.Append(property.Name);
                sb.Append(": ");
                sb.Append(this.GetType().GetProperty(property.Name).Name);
                sb.Append(System.Environment.NewLine);
            }
        }
        return sb.ToString();
    }
}

当前输出:

Prop1: System.String Prop1
Prop2: System.String Prop2

期望的输出:

Prop1: blah1
Prop2: blah2

我对其他解决方案持开放态度,它不必使用反射,它只需要产生所需的输出。

【问题讨论】:

  • 你需要使用GetValue方法。
  • 一般情况下,您不应使用ToString 返回所有属性值。相反,您可以提供一个方法GetPropertyInfo,如果您愿意的话。

标签: c# reflection overriding tostring


【解决方案1】:

这对我有用:

public class TestingClass
{
    public string Prop1 { get; set; }//properties
    public string Prop2 { get; set; }
    public void Method1(string a) { }//method
    public TestingClass() { }//const
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties())
        {
            sb.Append(property.Name);
            sb.Append(": ");
            if (property.GetIndexParameters().Length > 0)
            {
                sb.Append("Indexed Property cannot be used");
            }
            else
            {
                sb.Append(property.GetValue(this, null));
            }

            sb.Append(System.Environment.NewLine);
        }

        return sb.ToString();
    }
}

要使其随处可用,您可以创建一个扩展程序。
无法覆盖扩展中的方法,但它仍然可以简化您的生活。

public static class MyExtensions
{
    public static string ToStringExtension(this object obj)
    {
        StringBuilder sb = new StringBuilder();
        foreach (System.Reflection.PropertyInfo property in obj.GetType().GetProperties())
        {

            sb.Append(property.Name);
            sb.Append(": ");
            if (property.GetIndexParameters().Length > 0)
            {
                sb.Append("Indexed Property cannot be used");
            }
            else
            {
                sb.Append(property.GetValue(obj, null));
            }

            sb.Append(System.Environment.NewLine);
        }

        return sb.ToString();
    }
}

然后您可以在每个对象上调用ToStringExtension()
缺点是,它不适用于列表等,例如:

var list = new List<string>();
// (filling list ommitted)
list.ToStringExtension();
// output:
// Capacity: 16
// Count: 11
// Item: Indexed Property cannot be used

【讨论】:

  • 使用反射的性能怎么样? Hanselman 的实现如何hanselman.com/blog/…
  • 也适用于可空属性 public int?道具2 { 得到;放; } 还是 公共日期时间? Prop3 { 得到;放; } ?
【解决方案2】:

这是一个扩展,它会报告标准类型,如字符串、int 和 Datetime,但也会报告字符串列表(在下面的 AccessPoints 中显示,上面的答案无法处理)。注意输出是对齐的,例如:

Name         : Omegaman
ID           : 1
Role         : Admin
AccessPoints : Alpha, Beta, Gamma
WeekDays     : Mon, Tue
StartDate    : 3/18/2014 12:16:07 PM

下面是可以接受任何类型的扩展,只要它是一个类。然后它反映公共和私有属性,如果它们不为空,则报告它们。

public static string ReportAllProperties<T>(this T instance) where T : class
{

    if (instance == null)
        return string.Empty;

    var strListType = typeof(List<string>);
    var strArrType  = typeof(string[]);

    var arrayTypes   = new[] { strListType, strArrType };
    var handledTypes = new[] { typeof(Int32), typeof(String), typeof(bool), typeof(DateTime), typeof(double), typeof(decimal), strListType, strArrType };

    var validProperties = instance.GetType()
                                  .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                  .Where(prop => handledTypes.Contains(prop.PropertyType))
                                  .Where(prop => prop.GetValue(instance, null) != null)
                                  .ToList();

    var format = string.Format("{{0,-{0}}} : {{1}}", validProperties.Max(prp => prp.Name.Length));

    return string.Join(
             Environment.NewLine,
             validProperties.Select(prop => string.Format(format, 
                                                          prop.Name,
                                                          (arrayTypes.Contains(prop.PropertyType) ? string.Join(", ", (IEnumerable<string>)prop.GetValue(instance, null))
                                                                                                  : prop.GetValue(instance, null)))));
}

用法

myInstance.ReportAllProperties()

请注意,这是基于我的博客文章 C#: ToString To Report all Properties Even Private Ones Via Reflection,它对正在发生的事情提供了更可靠的解释。

【讨论】:

  • 也适用于可空属性 public int?道具2 { 得到;放; } 还是 公共日期时间? Prop3 { 得到;放; } ?
【解决方案3】:

我会使用 JSON,Serializer 会为您完成所有艰苦的工作:

    public static class ObjectExtensions
    {
        public static string ToStringEx(this object obj)
        {
            return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
        }
    }

【讨论】:

    【解决方案4】:

    这是我发现的,适用于大多数复杂类型(包括列表):

    public static string ToXml(object Obj, System.Type ObjType)
    {
        try
        {
            XmlSerializer ser;
            XmlSerializerNamespaces SerializeObject = new mlSerializerNamespaces();
            ser = new XmlSerializer((ObjType));
            MemoryStream memStream;
            memStream = new MemoryStream();
            XmlTextWriter xmlWriter;
            xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
            xmlWriter.Namespaces = true;
            XmlQualifiedName[] qualiArrayXML = SerializeObject.ToArray();
            ser.Serialize(xmlWriter, Obj);
            xmlWriter.Close();
            memStream.Close();
            string xml;
            xml = Encoding.UTF8.GetString(memStream.GetBuffer());
            xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
            xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
            return xml;
        }
        catch (Exception ex)
        { return string.Empty; }
    }
    

    用法:

    string classAasString = ClassToXml.ToXml(a, typeof(ClassA)); //whare ClassA is an object
    

    【讨论】:

      【解决方案5】:

      我自己遇到了这个问题,我正在寻找一个可以序列化为可读内容的选项。如果没有只读属性 xml 序列化可以给出一个可读的字符串。但是,如果有只读属性/字段,则不能选择 xml 序列化。

          public static string ToString(object serializeable)
          {
              var type = serializeable.GetType();
              try
              {
                  var sw = new StringWriter();
                  new XmlSerializer(type).Serialize(sw, serializeable);
                  return sw.ToString();
              }
              catch
              {
                  return type.FullName;
              }
          }
      

      【讨论】:

        【解决方案6】:

        所以我写了一个扩展方法,调用一个已经找出所有巫毒的库。

        “覆盖字符串 ToS​​tring()”与(我的)“ToStringDump”....

        在我展示代码之前,我喜欢扩展方法(在本例中为 ToStringDump)的原因.. 更好的是,我不必用 ObjectDump 引用来混淆我的 POCO/DTO 对象。我认为 POCO 和 DTO 应该“非常非常干净”,甚至在它们自己的组装中被隔离。这样,这些 poco/dto 对象就很容易共享了。

        public static class ObjectDumpAdapter
        {
            public static string ToStringDump(this object obj)
            {
                string returnValue = ObjectDumper.Dump(obj);
                return returnValue;
            }
        }
        

        我的 dotnet core csproj

        <Project Sdk="Microsoft.NET.Sdk">
        
          <PropertyGroup>
            <TargetFrameworks>netstandard2.0</TargetFrameworks>
          </PropertyGroup>
        
          <ItemGroup>
            <PackageReference Include="ObjectDumper.NET" Version="2.5.20033.1" />
          </ItemGroup>
        
        </Project>
        

        Nuget 链接:

        https://www.nuget.org/packages/ObjectDumper.NET/


        引用:

        ObjectDumper 是一个旨在将 C# 对象序列化为字符串的实用程序 用于调试和记录目的。

        (来自https://nugetmusthaves.com/Package/ObjectDumper.NET


        GitHub 链接:

        https://github.com/thomasgalliker/ObjectDumper

        【讨论】:

          猜你喜欢
          • 2012-08-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-30
          • 2020-03-09
          • 1970-01-01
          相关资源
          最近更新 更多