String.Format parameter order annoyance

12,864

Solution 1

Well, there's nothing in C# to do this automatically for you. You could always write your own method to do it, but frankly I'd find it less readable. There's a lot more thinking to do (IMO) to understand what your final line does than the previous one. When you hit the {2} you've got to mentally backtrack and replace the previous item with {3} to skip the {2} etc.

Personally I prefer code which takes a bit longer to type, but is clear to read.

Solution 2

As of Visual Studio 2015 you can side step this issue using Interpolated Strings (its a compiler trick, so it doesn't matter which version of the .net framework you target).

The code then looks something like this

string txt = $"{person.ForeName} is not at home {person.Something}"; 

I think it makes the code more readable and less error prone.

Solution 3

The function you request is not part of the framework. Here's a nice Extension Method I've found that provides named parameters c#. I think Marc Gravell posted it or one of those other SO gurus.

        static readonly Regex rePattern = new Regex(@"\{([^\}]+)\}", RegexOptions.Compiled);


    /// <summary>
    /// Shortcut for string.Format. Format string uses named parameters like {name}.
    /// 
    /// Example: 
    /// string s = Format("{age} years old, last name is {name} ", new {age = 18, name = "Foo"});
    ///
    /// </summary>
    /// <param name="format"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static string FN<T>(this string pattern, T template)
    {
        Dictionary<string, string> cache = new Dictionary<string, string>();
        return rePattern.Replace(pattern, match =>
        {
            string key = match.Groups[1].Value;
            string value;

            if (!cache.TryGetValue(key, out value))
            {
                var prop = typeof(T).GetProperty(key);
                if (prop == null)
                {
                    throw new ArgumentException("Not found: " + key, "pattern");
                }
                value = Convert.ToString(prop.GetValue(template, null));
                cache.Add(key, value);
            }
            return value;
        });
    }

Solution 4

Even though C# can't do this for you, the tool could help here.

Resharper for example warns you if you have more parameters in the string than after the string. I looked if parameter reordering in Resharper is supported but in this case it isn't (R# supports changing method signature but that doesn't help here).

Look at Code Rush from DevEx. That tool very likely has what you need.

Solution 5

I know this is old, I agree with Jon. Even with a large format string (see code example below), it still only takes me less then 1 minute to redo the index locations of the items if I have to add something, and I find it more maintainable and readable then trying to create a method to automate the process. The problem with automation for this is when I try to look at the code a few weeks later.. you cannot just figure it out at first glance. Also, once you learn Visual Studio well and learn to use things like block edit mode, and some of the other advanced features, you can be quite productive.

//-----------------------------------------------------------------------------
// <copyright file="ShellForm.cs" company="DCOM Productions">
//     Copyright (c) DCOM Productions.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------

string updateCommandText = string.Format("UPDATE `moh`.`moh` SET ageact = '{0}', branch = '{1}', cemetary = '{2}', citation = '{3}', citycement = '{4}', cdateact = '{5}', cdateaward = '{6}', cdatebirth = '{7}', cdatedeath = '{8}', namefirst = '{9}', namelast = '{10}', placeact = '{11}', placeenter = '{12}', presat = '{13}', presby = '{14}', rankact = '{15}', rankawd = '{16}', rankhigh = '{17}', synopsis = '{18}', unit = '{19}', war = '{20}', imgfile = '{21}' WHERE ID = '{22}'",
    /* {0}  */ uxAgeAct.Text.Replace("'", "''"),
    /* {1}  */ uxBranch.Text.Replace("'", "''"),
    /* {2}  */ uxCemetary.Text.Replace("'", "''"),
    /* {3}  */ uxCitation.Text.Replace("'", "''"),
    /* {4}  */ uxCityCemetary.Text.Replace("'", "''"),
    /* {5}  */ uxDateAct.Text.Replace("'", "''"),
    /* {6}  */ uxDateAward.Text.Replace("'", "''"),
    /* {7}  */ uxDateBirth.Text.Replace("'", "''"),
    /* {8}  */ uxDateDiceased.Text.Replace("'", "''"),
    /* {9}  */ uxNameFirst.Text.Replace("'", "''"),
    /* {10} */ uxNameLast.Text.Replace("'", "''"),
    /* {11} */ uxPlaceAct.Text.Replace("'", "''"),
    /* {12} */ uxPlaceEnter.Text.Replace("'", "''"),
    /* {13} */ uxPresentedAt.Text.Replace("'", "''"),
    /* {14} */ uxPresentedBy.Text.Replace("'", "''"),
    /* {15} */ uxRankAct.Text.Replace("'", "''"),
    /* {16} */ uxRankAwarded.Text.Replace("'", "''"),
    /* {17} */ uxRankHigh.Text.Replace("'", "''"),
    /* {18} */ uxSynopsis.Text.Replace("'", "''"),
    /* {19} */ uxUnit.Text.Replace("'", "''"),
    /* {20} */ uxWar.Text.Replace("'", "''"),
    /* {21} */ uxImgFile.Text.Replace("'", "''"),
    /* {22} */ dataRow["ID"].ToString());
Share:
12,864
Jamie Kitson
Author by

Jamie Kitson

SOreadytohelp

Updated on July 04, 2022

Comments

  • Jamie Kitson
    Jamie Kitson almost 2 years

    it's really annoying how C# seems to force you to explicitly name the index of every parameter in String.Format, if you want to add another parameter somewhere you either have to re-index the string or put your new parameter at the end.

    Is there a way to get C# to do this automatically?

    e.g. (I know this is pointless pedants, it's just an example :)

    I start with:

    String.Format("{0} {1} {1} {2} {3}", a, b, c, d)
    

    if I want to add a parameter at the beginning I can do one of the following:

    String.Format("{4} {0} {1} {1} {2} {3}", a, b, c, d, e)
    String.Format("{0} {1} {2} {2} {3} {4}", e, a, b, c, d)
    

    in Delphi for example I could do the equivalent of this:

    String.Format("{} {} {} {2} {} {}", e, a, b, c, d)
    
  • Richard
    Richard over 14 years
    +1: Maintainability should always win over one off savings when originally writing.
  • Daniel Elliott
    Daniel Elliott over 14 years
    +1 "Computer programs should be designed for humans to read and are incidentally run by computers"
  • Jamie Kitson
    Jamie Kitson over 14 years
    IMHO readability is solved by putting the parameters on the next line.
  • Jon Skeet
    Jon Skeet over 14 years
    @Jamie: That doesn't solve readability at all. It doesn't make it any clearer which argument corresponds to which replacement token.