Dynamic override of ToString() using Reflection

后端 未结 6 781
不知归路
不知归路 2021-01-04 14:12

I generally override the ToString() method to output the property names and the values associated to them. I got a bit tired of writing these by hand so I\'m looking for a d

相关标签:
6条回答
  • 2021-01-04 14:27

    Here is an extension which will report the standard types such as string, int and Datetime but will also report string lists (shown below in AccessPoints which the above answer could not handle). Note that the output is aligned such as:

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

    Below is the extension which takes in any type as long as its a class. It then reflects off of the public and private properties and if they are not null reports them.

    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)))));
    }
    

    Usage

    myInstance.ReportAllProperties()

    Note that this is based off my blog article C#: ToString To Report all Properties Even Private Ones Via Reflection which provides a more robust explanation of what is going on.

    0 讨论(0)
  • 2021-01-04 14:29

    This works for me:

    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();
        }
    }
    

    To make it available everywhere you can create an Extension.
    It's not possible to override methods in an Extension, but still it should simplify your life.

    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();
        }
    }
    

    You can then call ToStringExtension() on every object.
    Downside is, it doesn't work perfectly for lists etc., example:

    var list = new List<string>();
    // (filling list ommitted)
    list.ToStringExtension();
    // output:
    // Capacity: 16
    // Count: 11
    // Item: Indexed Property cannot be used
    
    0 讨论(0)
  • 2021-01-04 14:30

    This is what I found, that works with most compicated-types (including List):

    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; }
    }
    

    usage:

    string classAasString = ClassToXml.ToXml(a, typeof(ClassA)); //whare ClassA is an object
    
    0 讨论(0)
  • 2021-01-04 14:31

    I would use JSON, Serializer will do all the hard work for you:

        public static class ObjectExtensions
        {
            public static string ToStringEx(this object obj)
            {
                return JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
            }
        }
    
    0 讨论(0)
  • 2021-01-04 14:38

    I ran into this myself where I am looking for an option to serialize into something readable. If there are no read only properties xml serialization can give a readable string. However if there are read only properties / fields then xml serialization is not an option.

        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;
            }
        }
    
    0 讨论(0)
  • 2021-01-04 14:46

    So I wrote an extension method that calls a library that has already figured out all the voodoo.

    "override string ToString()" vs (my) "ToStringDump"....

    Before I show the code, the reason I like the extension method (ToStringDump in this case).. better, is that I don't have to riddle my POCO/DTO objects with ObjectDump references. I believe POCOs and DTOs should be "very very clean", and even isolated in their own assembly. This way, these poco/dto objects are easily shared.

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

    My 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 link:

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


    Quote:

    ObjectDumper is a utility which aims to serialize C# objects to string for debugging and logging purposes.

    (from https://nugetmusthaves.com/Package/ObjectDumper.NET )


    GitHub link:

    https://github.com/thomasgalliker/ObjectDumper

    0 讨论(0)
提交回复
热议问题