Reputation: 11420
I am using a 3rd party SDK that has a class with multiple constructors. It has a parameterless constructor, and has another constructor with all of it's properties set to default values (AKA optional arguments). Here's an example:
public class MyClass
{
public MyClass() { }
public MyClass(int i = 1) { }
}
It appears to me that if I create an instance of this class via new MyClass()
it will simply use the parameterless constructor. So how/why on earth could I ever call the one with the optional arguments?
The annoying thing is, the second one does additional setup of the class that I need to invoke, but I don't need to change the default value of the argument. So it seems my only option is to just hardcode the value of the first argument to be what is marked as the default value, just so I can use that constructor. i.e. In my simple example above, I am forced to do new MyClass(1)
.
This is kind of just a rant about a poorly designed SDK. But I thought I would toss this out there to see if you guys knew of anything extra I could do to call the 2nd ctor without hardcoding to the same default value.
Upvotes: 0
Views: 955
Reputation: 1
I think, if I am right, when you have no attributes—for example, private int i;
—your compiler would use the constructor with the parameter since your class now has an attribute. Since you do not have an attribute, however, your compiler runs the constructor with no parameters. Try this out.
public class MyClass
{
private int i;
public MyClass(){}
public MyClass(int i)
{
this.i = i;
}
}
Upvotes: 0
Reputation: 7622
I can't speak for the original developer's intent, but the most charitable reason why someone might implement this design is so that you can optionally specify named parameters in C#. So, in your example, you might say:
var myClass = new MyClass(i: 1);
Obviously, that doesn't provide any value with a single parameter, but certainly has some advantages if you have a longer list:
public MyClass {
public MyClass(int a=1, int b=2, int c=3 …) { }
}
As then you can say:
new MyClass(c: 5);
That said, even if that was their intent—and I'm not confident that it was—a far more common pattern in these cases is to use an options object, or to specify the values of the properties using the the object initialization syntax:
var myClass = new MyClass {
B = 5,
H = 10
}
Regardless, as far as your rant, I agree that this sounds like a really poorly considered design—and especially since the overload includes additional setup logic. That's certainly not a best practice.
It's not pretty, but the best way to force it to use the overload is to set one of the parameters that has an obvious default—such as null
—using the named parameter approach. That way, you're getting the "correct" overload, but don't need to specify all of the values.
Outside of including at least one of the parameters, though, there isn't a(n easy) way to force C# to use the overload.
Upvotes: 2
Reputation: 38850
Your options seem to be:
a) Contact the author of the library and explain the situation that the two constructors don't behave in the same way. Perhaps they could fix the behaviour for you. If it's open source, perhaps you could fix it yourself and submit a pull request.
b) Pass the default value when you initialize the class:
var obj = new MyTest(1);
c) Create a factory method that can use the constructor with the optional value, but uses reflection (slower):
public static Test ConstructTest(int? i = null)
{
// find the constructor that takes an `int`:
ConstructorInfo constructorMethod = typeof(Test).GetConstructor(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, new Type[] { typeof(int) }, null);
if (constructorMethod == null)
{
throw new Exception("desired constructor not found"); // replace with specific exception
}
ParameterInfo iParameter = constructorMethod.GetParameters().Single();
if (!i.HasValue && !iParameter.HasDefaultValue)
{
throw new Exception("no parameter specified, no default value"); // replace with specific exception
}
i = i ?? (int)iParameter.DefaultValue; // use the supplied parameter or the default value
return (Test)constructorMethod.Invoke(new object[] { i }); // invoke the constructor
}
The risk with method c) is that the library could change, and then your code would break at runtime, not compile time. If you go with this option, I would highly recommend writing a unit test around this case in order to verify it each time you update the third party library, or before release.
Upvotes: 3
Reputation: 19384
Sure, a class behavior is totally up to its provider, i.e. 3rd party in your case. But do not disregard that it has multiple constructors. It is possible that your provider has total disaster of developers who created something that does not makes sense. But usually there is intent in everything. If you r class has multiple constructors, e.g.
public MyClass() {. . . }
public MyClass(int x) {. . . }
most likely it would a property X
that is optional or had default value and you can set it up like this
public MyClass() {X = 5 }
But the bottom line is - talk to your vendor
However, this is not all. Some classes are designed to do same thing in different ways
public MyClass(DataTable dt) {. . . }
public MyClass(DataReader dr) {. . . }
public MyClass(IEnumerable<poco> modelList) {. . . }
Upvotes: 0