C# String.Format optional parameters

故事扮演 提交于 2019-12-01 07:03:44

It depends on what you mean by "optional parameter".

If you want to automatically replace null with a default value, the easiest way to do that is to use the null coalescing operator inside the arguments:

String.Format("{0} {1}", 
              "a",
              someNullableVariableContainingB ?? "default value");

If you want to reuse the same formatting string for multiple String.Format invocations, e.g.

var myFormatString = "{0} {1}";
var string1 = String.Format(myFormatString, "a", "b");
var string2 = String.Format(myFormatString, "a");

then you are out of luck: String.Format will throw an exception if too few arguments are provided, and there's no way to specify an "optional parameter" inside the format string. You will have to use something other than String.Format, for example, a custom method which replaces missing arguments with their intended default values.

This extension method is not limited to a fixed number of parameters. That is it will work with strings like "{0}", but also "{0} {1}", "{0} {1} {2}" and so on. The minor disadvantage is that first you give optional argument and then the non-optional. Should be the other way around but unfortunately the nature of the params keyword prohibits that. The major disadvantage is that it ignores the number in the curly braces (though the solution can be reworked to include that as well).

    public static string FormatOpt(this string s, string optional, params string[] param)
    {
        StringBuilder result = new StringBuilder();
        int index = 0;
        bool opened = false;
        Stack<string> stack = new Stack<string>(param.Reverse());

        foreach(var c in s)
        {
            if (c == '{')
            {
                opened = true;
                index = result.Length;
            }
            else if (opened && c == '}')
            {
                opened = false;
                var p = stack.Count > 0 ? stack.Pop() : optional;
                var lenToRem = result.Length - index;
                result.Remove(index, lenToRem);
                result.Append(p);
                continue;
            }
            else if (opened && !Char.IsDigit(c))
            {
                opened = false;
            }

            result.Append(c);
        }

        return result.ToString();
    }

And there are expected results:

string res1 = "result: {0}, {1}, {2}, {3}".FormatOpt("optional", "first param", "second param");
// result: first param, second param, optional, optional

string res2 = "result: {0}, {1}, {2}, {3}".FormatOpt("optional", "first param");                 
// result: first param, optional, optional, optional

string res3 = "result: {0}, {1}, {2}, {3}".FormatOpt("optional");                                
// result: optional, optional, optional, optional
  private static Regex curlyBracketRegex = new Regex("\\{(.+?)\\}");

  private static string OptionalFormat(string formatString, params object[] args)
  {
        var numberOfArguments = curlyBracketRegex.Matches(formatString).Count;

        var missingArgumentCount = numberOfArguments - args.Length;
        if (missingArgumentCount <= 0) //more argument or just enough
            return string.Format(formatString, args);

        args = args.Concat(Enumerable.Range(0, missingArgumentCount).Select(_ => string.Empty)).ToArray();
        return string.Format(formatString, args);
  }

This method above works with simple format strings. The regex detects the curly brackets. If the matches count is higher than the number of the passed args a new array will be created combining the original array with empty strings.

Example: OptionalFormat("{0}#{1}", "apple") //apple#

You could create an extension method:

public static string MyFormat(this string s, int i, params string[] args){
    var t = new List<string>(args);
    for(var c = t.Count; c < i; ++c)
        t.Add(String.Empty); // or other default

    return String.Format(s, t.ToArray());
}

and call:

"{0}:{1} - {2},{3},{4}".MyFormat(5, "ping", "fnord");

It does however force you to specify an arguments in order, so you cannot skip {3} if you want to set {4}. You could however add:

for(var x = 0; x < args.Count; ++x){
    if(args[x] == null) args[x] = String.Empty;

and call:

"{0}:{1} - {2},{3},{4}".MyFormat(5, "ping", null, "pong");

to set {0} and {2}, but default {1}, {3} and {4} to String.Empty;

You could go for automagically determining the i, but it's so much easier like this.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!