7

I have:

public static string Format(this string text, params object[] args)
{
   return string.Format(text, args);
}

So I can do:

"blablabla {0}".Format(variable1);

Is it a good/bad? Can it become even shorter? I want to have strings build seamlessly, like writing the text without worrying before or after of parameters and stuff:

// bad
return "date: " + DateTime.Now.ToString("dd.MM.yyyy") + "\ntime: " + DateTime.Now.ToString("mm:HH:ss") + "\nuser: " + _user + " (" + _status + ")";

// better, but you have to deal with order of {0}...{n} and order of parameters
return string.Format("date: {0}\ntime: {1}\user: {2} ({3})", ...);

// ideal
return "date: {DateTime.Now{dd:MM:yyyy}}\ntime: {...}\nuser: {_user} ({_status})";
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Sinatr
  • 20,892
  • 15
  • 90
  • 319

4 Answers4

3

Well, one bad thing there is that by only having a single params object[] method you force an extra array allocation per-call.

You may notice that string.Format has a range of overloads for taking low numbers of arguments (these are very commonly used) - I would suggest duplicating them.

Your "ideal" scenario could be done by re-writing the string.Format method, but you'd need to pass in the values, i.e.

return "date: {date}\ntime: {...}\nuser: {_user} ({_status})"
     .Format(new { date = DateTime.Now, _user, _status });

(and using your own custom Format method, or one like this) - but note this forces a new object instance per call.

Actually, at one point the mono compiler had an experimental flag to enable this directly. I don't know if it is maintained.

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Cool idea about putting into a string my identifiers and then replace them with the values i need. Thanks! – Sinatr Jul 12 '13 at 10:27
3

I also use similar extension method, and I like it, I also specify culture info in the extension method which is fixed for the system in my case.

public static string Format(this string formatTemplate, params object[] args)
{
   return string.Format(SysSettings.CultureInfo, formatTemplate, args);
}

Usages:

return "date: {0:dd.MM.yyyy}\ntime: {1:mm:HH:ss}\nuser: {2} ({3})".Format(DateTime.Now, DateTime.Now, _user, _status);
Morbia
  • 4,144
  • 3
  • 21
  • 13
  • Awesome idea with culture, thanks. I was pushed to use neutral culture for decimal point when using `double` or `float` and never when using `int`, and with extension method I can kill everything in 1 shoot! – Sinatr Jul 12 '13 at 10:33
  • This will give a compile error if you ever try to pass 2 arguments with the first being a string. `"{0} - {1}".Format("example", 1);` as it will match the String.Format static signature instead of the extension – GP89 Mar 16 '17 at 10:51
3

It's doesn't quite match your ideal, but something like this might work for you:

public static class Extensions
{
    public static string Format(this object data, string format)
    {
        var values = new List<object>();
        var type = data.GetType();
        format = Regex.Replace(format, @"(^|[^{])\{([^{}]+)\}([^}]|$)", x =>
        {
            var keyValues = Regex.Split(x.Groups[2].Value,
                                        "^([^:]+):?(.*)$")
                                    .Where(y => !string.IsNullOrEmpty(y));

            var key = keyValues.ElementAt(0);
            var valueFormat = keyValues.Count() > 1 ?
                                ":" + keyValues.ElementAt(1) :
                                string.Empty;


            var value = GetValue(key, data, type);

            values.Add(value);
            return string.Format("{0}{{{1}{2}}}{3}", 
                                    x.Groups[1].Value, 
                                    values.Count - 1, 
                                    valueFormat, 
                                    x.Groups[3].Value);
        });


        return string.Format(format, values.ToArray());
    }

    private static object GetValue(string name, object data, Type type)
    {
        var info = type.GetProperty(name);
        return info.GetValue(data, new object[0]);
    }
}

This should allow you to do this sort of formatting on any object:

new {Person = "Me", Location = "On holiday"}
    .Format("{Person} is currently {Location}");

It will also allow you to add some formatting:

new {Person = "Me", Until = new DateTime(2013,8,1)}
    .Format("{Person} is away until {Until:yyyy-MM-dd});

How is this for you? I'm sure the code could be improved in terms of efficiency but it works!

Aaron Janes
  • 304
  • 3
  • 11
  • Your idea is to generate array of args *before* calling Format and to put *name* of variable into the format string instead of `{0}`, which is cool I think. Thanks. – Sinatr Jul 12 '13 at 11:33
  • Yes, except the Format extension method is called on an object rather than an array. In my examples this is an instance of an anonymous type, but really it could be any object, such as a domain object: (eg: var person = new Person("Aaron","Janes"); person.Format("{LastName}, {FirstName}");) – Aaron Janes Jul 15 '13 at 10:08
  • Currently the extension method I knocked up does not support indexed properties so: new { Items = new [] {"One", "Two"} }.Format("First item: {Items[0]}"); will not work. However it should be relatively trivial to add support for indexers. – Aaron Janes Jul 15 '13 at 10:10
1

It depends if you're coding alone or have a team. In a team this is a pretty bad idea as everyone will have to learn this method.

Another problem is in Format with args on strings that accidentally contains braces with wrong index like {1} instead of {2}. This way just bad string will crash entire application. I used something similar for my logging and had to use try-catch for FormatExceptions.

Taylan Aydinli
  • 4,333
  • 15
  • 39
  • 33
ne2dmar
  • 504
  • 4
  • 17
  • If i put try/catch inside extension method and provide some other way to signalize about programming mistakes (to example, by adding to the end of string something like "omg, parameter {10} is missing"), then extension method is a big advantage (so i don't have to duplicate try/catch for every format). And every team has their own "styling" or common solutions for common cases. If you would be confused by syntax - you can see quickly what is it directly in the Visual Studio. So I don't see a big problem here. – Sinatr Jul 12 '13 at 10:31