Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String.Format extension method [closed]

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})";
like image 205
Sinatr Avatar asked Jul 12 '13 09:07

Sinatr


2 Answers

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!

like image 23
Aaron Janes Avatar answered Oct 03 '22 15:10

Aaron Janes


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.

like image 55
Marc Gravell Avatar answered Oct 03 '22 15:10

Marc Gravell