Ambiguous constructor due to default values
-
Tuesday, February 21, 2012 5:00 PM
So I've hit a bit of an issue that I'd never encountered before and was wondering if there is a way around it that leaves the intent of my code intact. I have a class with three constructors, which I'll simplify below:
public ClassA(string a, string b, string c = null, string d = null, string e = null)
public ClassA(string a, string b, string c = null, string d = null)
public ClassA(string a, string b, dictionary<string, string> c = null)
So I have three classes with different signitures, but if I try to just call
ClassA foo = new ClassA("foo", "bar");
I get an error saying it is an ambiguous constructor call. I understand why, but my question is without having to enter a bunch of nulls (and thus negating the usefulness of the default values set) is there a way to set one of these constructors as the default constructor to use in this case?
- Moved by CoolDadTxMVP, Moderator Tuesday, February 21, 2012 5:54 PM Language related (From:Visual C# General)
All Replies
-
Tuesday, February 21, 2012 5:19 PM
I can think of two possibilities:
1) The second constructor does the same thing as the first. You should remove the second constructor, and have the third parameter not optional in one of the two remaining constructors.
2) The second constructor does something useful. The first and third constructors shouldn't have any optional parameter.
- Edited by Louis.frMicrosoft Community Contributor Tuesday, February 21, 2012 5:22 PM
- Proposed As Answer by Rudedog2MVP, Moderator Wednesday, February 22, 2012 12:03 AM
- Marked As Answer by Leo Liu - MSFTModerator Wednesday, February 29, 2012 4:07 AM
-
Tuesday, February 21, 2012 5:24 PM
No.
Also, merely adding explicit nulls wouldn't help in this case. new ClassA("foo", "bar", null, null) is still ambiguous.
I think the million dollar question is what does the 4 string constructor do that cannot simply be done by the 5 string constructor version.
Also, I tend to use default arguments very sparingly (and I come from a C++ background where I have had them for nearly two decades). I find that they tend to confuse more than they simplify. I know any time I wind up with more than one default argument I flag the code as potentially needing a refactor.
- Marked As Answer by Leo Liu - MSFTModerator Wednesday, February 29, 2012 4:07 AM
-
Tuesday, February 21, 2012 5:27 PM
I dont find constructors are equal one to antoher (of all three).
Remove "= null" from all of the parameters. If I do it like you I get an error "Default parameter specifiers are not permitted".
Example:
class Program { static void Main(string[] args) { ClassA a1 = new ClassA("a", "b", null, null, null); ClassA a2 = new ClassA("a", "b", "c", null); Dictionary<string, string> dic =new Dictionary<string,string>(); dic.Add("keyValue", "valueValue1"); dic["keyValue"] = "valueValue2"; ClassA a3 = new ClassA("a", "b", dic); } } class ClassA { public ClassA(string a, string b, string c, string d, string e) { } public ClassA(string a, string b, string c, string d) { } public ClassA(string a, string b, Dictionary<string, string> c) { } }
Mitja
- Edited by Mitja BoncaMicrosoft Community Contributor Tuesday, February 21, 2012 5:28 PM
- Edited by Mitja BoncaMicrosoft Community Contributor Tuesday, February 21, 2012 5:31 PM
- Edited by Mitja BoncaMicrosoft Community Contributor Tuesday, February 21, 2012 5:32 PM
-
Tuesday, February 21, 2012 6:07 PM
Agreed to simon.
We even had this issue even before optional parameters.
I was working on a framework where I had to pass a null in the last parameter and it throwed me an error. I had to modify the whole code to make it up and running. Just adding overloaded methods and constructors for the sake of having options should be highly discouraged when it can be eliminated easily.
Planet Earth is at risk. Global warming is on a high tide.
Take Responsibility. Plant Trees. Keep your City Clean and Green.Mark all Helping Posts and Close your Threads. Keep the Forum Green.
- Arun Kumar Allu -
Tuesday, February 21, 2012 6:35 PM
Hi,
Or perhaps http://msdn.microsoft.com/en-us/library/w5zay9db.aspx (params keyword) depending on what is the intent of your code ?
Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".
-
Tuesday, February 21, 2012 7:36 PM
Sorry, in my simplification I may have made it more confusing. In the actual code it's not all strings. Basically one accepts a 2D array of strings, one accepts two seperate strings, and one accepts a dictionary.
Ok, I'll just get down to brass tacks here:
public ErrorLogger(string appName, LoggingMode mode = LoggingMode.Event, SqlConnection connection = null, string StoredProcedure = null, string errorParameterName = null, string SPParameters = null, string SPValues = null)
This is an example of one of the constructor headers. Basically this is an error logger. The logger has two modes: event logging or SQL logging. It defaults to Event logging where it stores everything in the windows application events log. However you can specify LoggingMode.SQL and then pass in all of the necessary SQL parameters to tell it the SQL connection, stored procedure name, etc. It also allows for an arbitrary list of stored procedure parameters to be specified, which can be passed in as 1 - A dictionary, 2 - a 2D array, or 3 - Two semicolon delimited strings (shown above). However all of these parameters are unnecessary if you're using the windows event logging instead, thus the default nulls.
Edit: Ok, after some thought I' m just going to split it into 4 constructors, the 3 SQL versions and the windows event version, and then in the SQL versions make all of the parameters mandatory and provide no default values.- Edited by MassaYoda Tuesday, February 21, 2012 7:49 PM
- Proposed As Answer by Rudedog2MVP, Moderator Wednesday, February 22, 2012 12:03 AM
- Marked As Answer by Leo Liu - MSFTModerator Wednesday, February 29, 2012 4:08 AM
-
Wednesday, February 22, 2012 5:28 PM
If I do it like you I get an error "Default parameter specifiers are not permitted".
Then you're probably not using C#4. -
Wednesday, February 22, 2012 11:45 PM
When you have several constructors like this (where you want to allow various default parameters) you might find that some well-named static factory methods can help
I often find that the different factory methods with different parameter lists can have more meaningful names than just "Create" - for example, CreateFromIdentifier(), CreateFromCollection() etc (the names depend very much on the class of course).
All the static factory methods can call a (possibly private) constructor which doesn't have default parameters.
In your example, you might be able to have:
ClassA.CreateFromStrings(string a, string b, string c = null, string d = null, string e = null);
ClassA.CreateFromDictionary(string a, string b, dictionary<string, string> c = null);
I can't come up with a name to differentiate your two different constructors which take string parameters; I have no context to decide - and neither would users of your class without additional documentation. For this reason, I think using an informatively-named static factory method would improve the code clarity.
- Edited by Matthew Watson Wednesday, February 22, 2012 11:45 PM
- Marked As Answer by Leo Liu - MSFTModerator Wednesday, February 29, 2012 4:08 AM

