Click to See Complete Forum and Search --> : Precedence of "params" parameters vs normal parameters


cjard
October 14th, 2008, 10:01 AM
Guys

Does anyone have a strategy they employ for providing overloads, one of which is a params, that potentially causes overload confusion?

I want to implement:


protected void A(string a){
A(a, "");
}

protected void A(string a, string b){
//do something
}

protected void A(string a, params object[] z){
A(string.Format(a, z));
}

protected void A(string a, string b, params object[] z){
A(string.Format(a, z), b);
}


And in calling code, existing we have stuff like:

A("The process failed")
A("The process failed: {0}", ex.Message)


I now want to upgrade:
A("The processed failed", "Errors Group");
A("The processed failed a status {1}: {0}", "Errors Group", ex.Message, obj.Status);


-
but of course, all that code that is:

A("The process failed: {0}", ex.Message)


Might call:
A(string, params object[] z)

And this is a silent problem, right?

Mutant_Fruit
October 14th, 2008, 01:49 PM
You do understand how params works, right? Because you can do that exact thing right now with the exact code you have already...



public string A (string a, params object[] formatting)
{
return string.Format (a, formatting);
}

A("The processed failed a status {2}: With message {1} : In group {0}", "Errors Group", ex.Message, obj.Status);


EDIT: Also, there's no way to execute the following code and actually have it do something useful:

string.Format (string.Format (a, z), b);

All instances of {0} will be replaced with z[0] in the first call which means in the outer call (when you combine with b), nothing will happen.

TheCPUWizard
October 14th, 2008, 02:14 PM
EDIT: Also, there's no way to execute the following code and actually have it do something useful:

string.Format (string.Format (a, z), b);

All instances of {0} will be replaced with z[0] in the first call which means in the outer call (when you combine with b), nothing will happen.

Yes, but if "a" contains "{{" and/or "}}", the the code may be insane, but still do something valid:


String a = "{0}0}} World";
String b ="Hello";
String z ="{";


Or if "Z" contains "{0}"


String a = "{0} World";
String b ="GoodBye";
String z = "{0} Cruel";


Please note that this is NOT a recommendation to use this coding style....

Mutant_Fruit
October 14th, 2008, 04:00 PM
Yes, but if "a" contains "{{" and/or "}}", the the code may be insane, but still do something valid:


String a = "{0}0}} World";
String b ="Hello";
String z ="{";


Or if "Z" contains "{0}"


String a = "{0} World";
String b ="GoodBye";
String z = "{0} Cruel";


Please note that this is NOT a recommendation to use this coding style....
Ah, good point! I tried thinking of how you could manage it but it looks like I missed the obvious ;) I didn't cop that the second string was embedded verbatim into the first, so the second string could easily contain a '{0}' and so be valid for a second call to string.Format.

Doh.

I'll just go hang my head in shame ;)

cjard
October 15th, 2008, 03:18 AM
OK, here's a little more detail.

A() is a logging function. It either takes a string to log, or it takes a string to format, and the format params. This is how it exists currently. I'm upgrading the code that exists out there.

Sometimes we have:

A("The error was: " + ex.Message +" and the ID was " + personID);


Sometimes we have:

A("The error was: {0} and the ID was {1}", ex.Message, personID);


Errors are stored in a Dictionary<string, int>, where the error message is the key and the count of the number of times it happened are the int value

-

Now because the log emails just end up as a mash of log messages in one long HTML box, I actually want to put them into groups (assigning the group when the message is raised). I also want two overloads to support the existing code:

A(string msg)
A(string msgFormat, params object[] p)

And with the group:
A(string msgFormat, string grp, params object[] p)


Now, I know that calling the latter, with no extra params results in a call being made with an object[0]
But.. I really dont want the existing code of:

A("The error was {0}", ex.Message);

Calling A(string, string, params object[])

Because then I'll end up with the exception message as my group name.


So, what options do I have?
1) Call the method something else [AGrouped(string, string, params object[])]
2) Test the input string to A(string msgFormat, string grp, params o[]) if msgFormat contains "{0" and grp is string and o[].Length == 0 then assume A(string, string) was meant to be called and call it/do the same actions as it
3) Create a custom class containing the error message, and a group, and have A(LogMessage m, params object[] p)
4) other..


Right now, i'm favouring 1. Actually I'm favouring just commenting out all the A() methods and using every compiler error to clean up the old logging calls, then having just one A(s,s,o[])..
..but I do want an overload for NO GROUP, so that there is no code clutter with A("error here {0}", "", ex.Message) - jsut lookis a mess..

Ahh. So, basically the questions remains:

What do you do when params would cause confusion with another method? WHich gets called?

Look at
string.Format(string, object) vs
string.Format(string, object, object) vs
string.Format(string, object, object, object) vs
string.Format(string, params object[])

I presume the 4th one is supplied for any number of params over 3, the other 3 overloads are higher precedence and remove the (cheap) cost of creating an array - mostly Format() is called with 3 or fewer params? Thus I also presume a params call is lowest in priority which means that:

A("Error {0}", ex.Message)

Will call one of:
A(string msg, string grp) [if this overload is provided - currently is but not really discussed thus far]
A(string msg, string grp, params object[] p)

Not:
A(string msgFormat, params object[] p)


And that's a problem

boudino
October 15th, 2008, 03:40 AM
I think that option 3 - to create a custom data class - is the right one.

cjard
October 15th, 2008, 04:07 AM
Mmmh, but then I have to instantiate the bloody thing:

A(new LogMessage("error {0}, count {1}", "Errors Group"), ex.message, abc.Count);

and now look, we have our params object[] outside the message to what it relates.. I dunno... it just looks a mess

I'm thinking it might be better to have some kind of log manager class, and a Log class that is a thin wrapper around a Dictionary<string, int>
LogManager can have a default property that returns the relevant Log, or it can just have a LogMessage(string, params object[]) that adds to the default Log. Log also has a LogMessage(string, params object[]).
LogManager can be asked for a string rep of all the logs it is managing


Ya. I'll do that! Cheers for the cardboard analysis :)