Question

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.

Was it helpful?

Solution

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

OTHER TIPS

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);
}

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()); 
    }
}

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'.".

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.

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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top