17

In the Java world we have Apache Commons' ToStringBuilder to help with creating toString() implementations.

Does anyone know of a decent free implementation for C#? Are there better alternatives I don't know about?

If no free implementation exists than I guess this question becomes more of a question of "What would make a good ToStringBuilder in C# 3?"

Off the top of my head:

  • It could offer both reflection and manual ToString string creation.

  • It would be really cool if it could make use of Expression trees.

Something like this..

 public override string ToString()
   {
      return new ToStringBuilder<Foo>(this)
         .Append(t => t.Id)
         .Append(t => t.Name)
         .ToString();
   }

Which would return:

 "Foo{Id: 1, Name: AName}"
  • It could use System.Reflection.Emit to precompile a ToString delegate.

Any other ideas?

UPDATE

Just to clarify ToStringBuilder is a different creature to StringBuilder.. I'm looking for something akin to the functionality of Apache Common's ToStringBuilder, it has features such as multi-line formatting, different styles and reflection base ToString creation. Thanks.

UPDATE 2

I've built my own. See here.

Community
  • 1
  • 1
chillitom
  • 24,888
  • 17
  • 83
  • 118
  • Perhaps you should have linked to the description of what ToStringBuilder does: http://commons.apache.org/lang/api/org/apache/commons/lang/builder/ToStringBuilder.html – Powerlord Mar 10 '10 at 14:44
  • @Powerlord, thanks I've updated the link, I guess not everyone loves grokking source code as much as me ;-) – chillitom Mar 10 '10 at 14:48
  • Fast C# 4 version added below: http://stackoverflow.com/questions/2417647/is-there-an-equivalent-to-javas-tostringbuilder-for-c-what-would-a-good-c-ver/4959343#4959343 – chillitom Feb 10 '11 at 15:53

8 Answers8

13

EDIT: OK, you want to use reflection so you don't have to type property names. I think this will get you what you're after:

// forgive the mangled code; I hate horizontal scrolling
public sealed class ToStringBuilder<T> {
    private T _obj;
    private Type _objType;
    private StringBuilder _innerSb;

    public ToStringBuilder(T obj) {
        _obj = obj;
        _objType = obj.GetType();
        _innerSb = new StringBuilder();
    }

    public ToStringBuilder<T> Append<TProperty>
    (Expression<Func<T, TProperty>> expression) {

        string propertyName;
        if (!TryGetPropertyName(expression, out propertyName))
            throw new ArgumentException(
                "Expression must be a simple property expression."
            );

        Func<T, TProperty> func = expression.Compile();

        if (_innerSb.Length < 1)
            _innerSb.Append(
                propertyName + ": " + func(_obj).ToString()
            );
        else
            _innerSb.Append(
                ", " + propertyName + ": " + func(_obj).ToString()
            );

        return this;
    }

    private static bool TryGetPropertyName<TProperty>
    (Expression<Func<T, TProperty>> expression, out string propertyName) {

        propertyName = default(string);

        var propertyExpression = expression.Body as MemberExpression;
        if (propertyExpression == null)
            return false;

        propertyName = propertyExpression.Member.Name;
        return true;
    }

    public override string ToString() {
        return _objType.Name + "{" + _innerSb.ToString() + "}";
    }
}

Example:

// from within some class with an Id and Name property
public override string ToString() {
    return new ToStringBuilder<SomeClass>(this)
        .Append(x => x.Id)
        .Append(x => x.Name)
        .ToString();
}

Behold, the behavior you're after:

class Thing {
    public int Id { get; set; }
    public string Name { get; set; }

    public override string ToString() {
        return new ToStringBuilder<Thing>(this)
            .Append(t => t.Id)
            .Append(t => t.Name)
            .ToString()
    }
}

void Main() {
    var t = new Thing { Id = 10, Name = "Bob" };
    Console.WriteLine(t.ToString());
}

Output:

Thing{Id: 10, Name: "Bob"}

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 2
    not nearly as good, this has string literals in it and won't survive refactoring – Ben Voigt Mar 10 '10 at 14:49
  • Thanks Dan that's a nice tight solution. Would be cool if we could expand on the Java version further and use expressions to get rid of the name string, that way the code would be refactor proof. – chillitom Mar 10 '10 at 15:02
  • @chillitom: I gave it a whack--take a look. – Dan Tao Mar 10 '10 at 15:59
  • Sweet! I was just doing something similar myself, this is a tidier than what I have so far. – chillitom Mar 10 '10 at 16:09
4

The original question concerned C# 3.5 but since then I've upgraded to C# 4.

I thought I'd share my new version here in case it's of benefit to others. It has all of the features mentioned in this thread and compiles at runtime to a fast Stringify method.

Get it here: ToStringBuilder

chillitom
  • 24,888
  • 17
  • 83
  • 118
  • hi chillitom, your ToStringBuilder looks pretty awesome, but it doesn't seem to handle generic Lists, like List, it just calls ToString() on the list without enumerating so you just get the full name of the list rather than the contents. I tried to enhance it to handle lists, but I couldn't get my head around using generics with linq Expressions. Did you ever enhance it to handle generic Lists? thanks! – matao Sep 16 '14 at 05:54
  • sorry, I haven't, but I would accept a pull request. The reflection API's let you take generic types and create reified types e.g. typeof(List<>).MakeGenericType(typeof(int)) === typeof(List) – chillitom Sep 17 '14 at 16:08
  • ok, I'm trying figure it out, when I do I'll send you a request. cheers! – matao Sep 18 '14 at 01:30
  • 1
    works perfectly with generic lists now, next step is to figure out how to get git working so I can send you a pull request. It may take a while... code is here: http://stackoverflow.com/a/25885391/115734 – matao Sep 18 '14 at 05:25
  • 1
    Excellent work! If figuring out git is too much work then Github will let you edit the file directly on the site, this creates a fork and branch and then gives you the option of raising a pull request. – chillitom Sep 18 '14 at 07:38
  • sent you a pull request :) – matao Oct 08 '14 at 02:12
  • no worries, glad to give back to the community that's given me so much. – matao Oct 09 '14 at 09:16
3

It might not be exactly what you are after since it is not free, but Resharper will do this. It is a fantastic plugin to visual studio that does a lot more than generate ToString. But it will do that to. put your cursor inside your class, hit alt+insert and choose formating members.

Mike Two
  • 44,935
  • 9
  • 80
  • 96
1

ObjectPrinter will do this for you with a number of customizable features. documentation is still a little rough, but we've been using it in production for years with great results.

1

Use .NET's StringBuilder.

Note that you'll have to provide a little template yourself.

E.g:

public StringBuilder ToStringBuilder<T>(T type) where T : IYourInterface
{
StringBuilder sb = new StringBuilder();
sb.append(type.key);
// more appends

return sb;
}

Provided a kinda generic way here. You'll be able to create your own neat solution with the System.Reflection namespace in .NET

Cheers

Faizan S.
  • 8,634
  • 8
  • 34
  • 63
  • thanks but I'm after something quite different, string builder is good but requires much more graft to make a pretty ToString than Java's Apache Commons ToStringBuilder does. – chillitom Mar 10 '10 at 14:41
  • saw it, you could try making your own ToStringBuilder method that envelops a stringbuilder.. – Faizan S. Mar 10 '10 at 14:42
  • The method you posted just returns a string. – Dan Tao Mar 10 '10 at 14:44
1

See this project...

http://commonsdotnet.codeplex.com/

mxmissile
  • 11,464
  • 3
  • 53
  • 79
  • thanks mxmissile, I knew something had to exist somewhere. It's a bit disappointing though, after looking at the source I'm guessing this targets C# versions 2 and below. It is very different from the Java class, it's just a bunch of static calls and requires the field names to be passed in as a list of strings, something much nicer is surely possible with C# 3. – chillitom Mar 10 '10 at 15:17
  • yea, just noticed that, I swear I have seen this somewhere, thought that was the place. Will keep looking, I could use something like this also. – mxmissile Mar 10 '10 at 15:24
0

I think that you are on to something with using Expressions. I read this article just the other night: Getting Information About Objects, Types, and Members with Expression Trees

I bet that you could combine those techniques with the code that Dan posted on this thread to get what you are after.

JMarsch
  • 21,484
  • 15
  • 77
  • 125
0

I don't know of any existing projects that would do what you want, but it wouldn't be that hard to implement the example you gave using lambdas.

Here's another [untested/uncompiled/possibly faulty] idea using anonymous delegates:

public override string ToString() {
    return this.ToString(x => {
        x.Append(t => t.Id);
        x.Append(t => t.Name);
    });
}

This ToString() overload would be written as an extension method, so you get a reference to the source object, and would accept an Action<T> where [T] is the type of source object. The ToString() method would then new up a string builder or internal object of some sort, execute the anonymous method passed in from the caller, and then wrap the result in any opening/closing text that is necessary.

To be clear, I haven't actually tried this. I just think its a little more flexible than the lambda-based example in the original question.

Seth Petry-Johnson
  • 11,845
  • 7
  • 49
  • 69