Make ToString() human readable in C#
Tags: cs, string
Published: 2019-05-28 18:23:18

---
title: Make ToString() human readable in C#
published: true
description: overriding the default ToString() behavior of selected classes
tags: cs, string
---
#Data Class in Kotlin
If you have ever used Kotlin and it’s `data class`, I’m certain you’ve stumbled upon one of its amazing features when calling the `ToString()` method.
> *The term `DataClass` in this Article just refers to my own implementation of overriding the `ToString()` method and* ***not*** *any other functionalities of the Kotlin `data class`! Bad naming on my side.*

Basically what it does is presenting the data within a class in a human-readable format.
Below is an example in Kotlin that shows both types:
```kotlin
class Person(val name: String, val age: Int)
// toString -> de.calucon.sample.Person@63e31ee

data class Person(val name: String, val age: Int)
// toString -> Person(name=Simon, age=21)
```

#Potential Usage
This can be a very useful feature in some circumstances, especially during development. Of course, you can use the integrated Debugger which shows you every single variable and its members, but for this, you also have to pause the execution of the application.
Where this might be beneficial to you depends on what types of projects you’re working on.

I’m currently working on a WPF Application that gets its data from a TCP connection with the server. Having the parsed data from the TCP socket printed out to the Debug window while trying to break the application is a huge benefit because I’m able to observe if the correct data got transmitted or if it didn’t even reach the transmitter.

# Data Class implementation in C&#35;

C# by default does not provide a `data class` as Kotlin does.
This is how it looks in C# when using a class or a struct:

```cs
public class Person
{
    public string Name;
    public int Age;
}

var person = new Person()
{
    Name = "Simon",
    Age = 21
};

Console.WriteLine("Person: {0}", person);
// Console Output: Person: Calucon.Sample.Person
```

What `Console.Write` does is calling the `ToString()` method for each object/parameter. 
We could now override the default `ToString()` method for each class and return a string that looks like the one from Kotlin, but this can be very time consuming and we have to deal with one big enemy: refactoring of variable names. This forces us to change the return value of the `ToString()` method to make the new field/property name match the output.

#Implementation

**First –** Query all `fields` and `properties` from our `Person` class using Reflection
```cs
// define which fields/properties to get
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;

foreach (var prop in GetType().GetProperties(flags))
{
    // code...
}

foreach (var field in GetType().GetFields(flags))
{
    // code...
}
```

**Second –** Get the field/property name and its value
*(code within the `foreach` loops above)*

```cs
// Property
string name = prop.Name;
object val = prop.GetValue(this);

// Field
string name = field.Name;
object val = field.GetValue(this);
```

`GetType()` returns the Type of the class implementing our `DataClass`, therefore the keyword `this` refers to the class that implements our `DataClass`.

**Third –** Let it look like Kotlin

Kotlin by default looks like this: 
`Type(field0=value0, field1=value1, ...)`

```cs
private void Analyze(ref StringBuilder sb, string name, object val)
{
    sb.AppendFormat("{0}={1}, ", name, val);
}
```

This would basically do the trick, but Kotlin also translates Arrays, Lists, etc. 
In C# we have an interface `ICollection` which we're going to use to identify Arrays, Lists, Dictionaries, …

```cs
private void Analyze(ref StringBuilder sb, string name, object val)
{
    // if it's an ICollection, parse the collection
    if (val is ICollection) val = ConvertCollection(val as ICollection);
    sb.AppendFormat("{0}={1}, ", name, val);
}

// Analyze any ICollection
private string ConvertCollection(ICollection enumerable)
{
    var sb = new StringBuilder();
    foreach (var item in enumerable) 
    {
        sb.AppendFormat("{0}, ", item);
    }
    var data = sb.ToString().TrimEnd(',', ' '); // remove trailing ', '
    return string.Format("[{0}]", data);
}
```

**Fourth –** Exclude Fields created by the compiler
*.NET generates some fields during compilation that are only for internal use. By using reflection we would also reveal these, but they do not provide us with anything useful.*
*They might look like this:* `<Array>k__BackingField`

```cs
private void Analyze(ref StringBuilder sb, string name, object val)
{
    // exclude fields generated by the compiler
    if (name.EndsWith("k__BackingField")) return;
    if (val is ICollection) val = ConvertCollection(val as ICollection);
    sb.AppendFormat("{0}={1}, ", name, val);
}
```

**Fifth –** Create our `DataClass` that overrides the `ToString()` method
*In this example, we’re overriding the default* `CultureInfo` *to* `InvariantCulture` *to compensate for different number formats.*

```cs
public class DataClass
{
    public override string ToString()
    {
        // store current culture and override it with InvariantCulture
        var currentCulture = CultureInfo.CurrentCulture;
        CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

        var sb = new StringBuilder();
        // code from 'First' and 'Second'

        // reset culture info
        CultureInfo.CurrentCulture = currentCulture;

        // return final string and remove traling ', ' from the StringBuilder
        return string.Format("{0}({1})", 
            GetType().Name, 
            sb.ToString().TrimEnd(',', ' ')
        );
    }

    private void Analyze(ref StringBuilder sb, string name, object val)
    {
        // code from 'Fourth'
    }

    private string ConvertCollection(ICollection enumerable)
    {
        // code from 'Third'
    }
}
```

---

# Code-Comparison

#### Kotlin
```kotlin
data class Person(val name: String, val age: Int)
// toString -> Person(name=Simon, age=21)
```

#### C&#35;
```cs
public class Person
{
    public string name;
    public int age;
}
// toString -> Person(name=Simon, age=21)
```

---

# DataClass Sourcecode

You can get the fully working class from my Gitlab [**here**](https://gitlab.com/snippets/1860946)

- Automatically generated fields by the compiler are excluded
- Objects implementing `ICollection` are automatically processed.
This includes objects like *Arrays, Lists, Dictionaries, …*
- Numbers are represented using the `InvarriantCulture` of .NET

It also includes `static` fields and properties by default. If you want to remove them, just edit the `DataClass` on **line 9** in the snippet:

```cs
// with static fields/properties
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;

// without static fields/properties
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
```

---

# Final thoughts

Sometimes I get the feeling like I'm the only one who sometimes prefers to have all/some variables printed out in a human-readable format. I'm really curious how many of you are sometimes thinking the same and override the default `ToString()` method. I'm glad if this helps at least one single person :)

*Also this is my first blog post ever. I'd be very grateful for any feedback you can give me to further improve my writing <3*
*You can read the original article [here](https://blog.calucon.de/programming/cs/data-class-cs/).*