String.Format() - Repassing params but adding more parameters
Asked Answered
O

6

6

I would like to do something like that:

public string GetMessage(params object otherValues[]) {
    return String.Format(this.Message, this.FirstValue, otherValues);
}

So, I would like to repass an array of params to String.Format() but adding a new parameter.

What would be the best way to do that, knowing that we could "rebuild" a new array of objects and this doesn't seems good.

Oniskey answered 8/5, 2012 at 17:41 Comment(0)
N
17
public string GetMessage(params object[] otherValues)
{
    return String.Format(this.Message, new[] { this.FirstValue }.Concat(otherValues).ToArray<object>());
}
Neu answered 8/5, 2012 at 17:45 Comment(1)
Unfortunatelly the ToArray() builds a new structure.Oniskey
C
3

You could use the Concat and ToArray extension methods:

public string GetMessage(params object[] otherValues) 
{
    var values = new[] { this.FirstName }.Concat(otherValues).ToArray();
    return String.Format(this.Message, values);
}
Candlepin answered 8/5, 2012 at 17:43 Comment(2)
That's exactly what I was looking at :-) It won't. Adding it. Thanks.Candlepin
Will that put this.FirstName before the first element of otherValues like the OP had in their question?Knighterrant
M
1

In case there are often few other parameters, I would use the existing overloads:

public string GetMessage(params object[] otherValues) {
    if (otherValues == null) return string.Format(this.Message, this.FirstValue);

    switch (otherValues.Length)
    {
        case 0:
            return string.Format(this.Message, this.FirstValue);
        case 1:
            return string.Format(this.Message, this.FirstValue, otherValues[0]);
        case 2:
            return string.Format(this.Message, this.FirstValue, otherValues[0], otherValues[1]);
        default:
            return string.Format(this.Message, new[] { this.FirstValue }.Concat(otherValues).ToArray()); 
    }
}
Malocclusion answered 8/5, 2012 at 17:58 Comment(0)
S
1

Preprocess Message

If you don't want a creation of a new array in every GetMessage(...) call you can insert FirstValue into Message at the beginning once a time. And then GetMessage(...) just uses the otherValues parameter for string.Format(...).

The Message property is initialized once after FirstValue is set, e.g. in constructor or in an init method like so:

void InitMessage()
{
    Message = String.Format(Message, FirstValue, "{0}", "{1}", "{2}", "{3}", "{4}");
}

The InitMessage method initializes first index in Message with FirstValue and the rest of indexes with "{index}", i.e. "{0}", "{1}", "{2}",... (It is allowed to have more params elements than message indexes).

Now GetMessage can call String.Format without any array operations like so:

public string GetMessage(params object[] otherValues)
{
  return String.Format(Message, otherValues);
}

Example:

Assume following property values:
this.Message = "First value is '{0}'. Other values are '{1}' and '{2}'." and this.FirstValue = "blue".

InitMessage changes Message to:
"First value is 'blue'. Other values are '{0}' and '{1}'.".

GetMessage call
GetMessage("green", "red")

results in
"First value is 'blue'. Other values are 'green' and 'red'.".

Snore answered 8/5, 2012 at 19:30 Comment(2)
@brernder: Interesting approach. Can be useful in others problems. But as you are using Concat(), it seems to be more work, as a direct use of Concat() in Format should solve it, too.Oniskey
You are right. I have updated the answer by just adding "{...}" parameters to String.Format call in InitMessage method.Snore
O
0

Another messy way is to hack your way around the formatting using RegEx if you really can't create another structure for the array.

private string FormatEval(Match m)
{
    int val = -1;
    string formatted = m.Value;
    if (int.TryParse(m.Groups["index"].Value, out val))
        formatted = val == 0 ? this.FirstValue : "{" + (val - 1).ToString() + "}";
    return formatted;
}

public string GetMessage(params object[] otherValues)
{
    string format = Regex.Replace(this.Message, @"\{(?<index>\d+)\}", FormatEval);
    return string.Format(format, otherValues);
}

Basically just parse the format string for the formatting tokens ({0}, {1}) etc. And decrement their index. If the token is {0} originally, replace it with the this.FirstName string.

Essentially what this is doing is performing the first step of String.Format manually, and then passing the resulting string to the REAL String.Format method to finish off.

Orang answered 10/5, 2012 at 7:4 Comment(0)
S
0

Pass discrete elements

To avoid array creation in everey GetMessage call you could pass otherValues by its discrete elements:

public string GetMessage(params object[] otherValues)
{
  return String.Format(Message,
                       FirstValue,
                       GetOrDefault(otherValues, 0),
                       GetOrDefault(otherValues, 1),
                       GetOrDefault(otherValues, 2),
                       GetOrDefault(otherValues, 3),
                       GetOrDefault(otherValues, 4));
}

private object GetOrDefault(object[] otherValues, int index)
{
  if (otherValues == null)
    return null;

  if (index < otherValues.Length)
    return otherValues[index];

  return null;
}
Snore answered 10/5, 2012 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.