Search code examples
c#genericsoverloadingconstructor-overloading

Which Constructor takes precedence when there are two matches for a Generic Class's Constructor


What determines which constructor is used if a Generic class has 2 constructors: One takes (T data), and another (string errorMessage).

And an instance is created with type string?

For me it seems to be using the constructor with parameter (string errorMessage) instead of (T data).

  • I'm wondering why?
  • And is there a way to make it use the generic (T data) constructor instead?

Luckily I don't need to in my case, but I don't know how I would.

public class GenericClass<T>
{
    public T Data { get; set; }
    public string ErrorMessage { get; set; }

    public GenericClass(T data)
    {
        Data = data;
    }

    public GenericClass(string errorMessage)
    {
        ErrorMessage = errorMessage;
    }
}

New instance is created with either:

new GenericClass<string>("some error");

Or:

new GenericClass<string>("some data");

I thought it would give an Ambiguous Constructor error, but it doesn't. It always uses the (string errorMessage) constructor.


Solution

  • Your problem is not specific to constructors. The same behaviour can be observed in case of method overloading. For example:

    public class ClassA
    {
        public static void MethodA<T>(T data)
        {
            Console.WriteLine("Generic version has been called");
        }
    
        public static void MethodA(string errorMessage)
        {
            Console.WriteLine("String version has been called");
        }
    }
    

    The related documentation can be found here: Overload resolution.


    You can enforce the call of the other constructor if you are taking advantage of named parameters feature of C#:

    new GenericClass<string>(data: "some data");
    

    Of course this technique works only if the parameter names are different.
    So, this won't work if you would define your class like this:

    public class GenericClass<T>
    {
        public T Data { get; set; }
        public string ErrorMessage { get; set; }
    
        public GenericClass(T _)
          => Data = _;
    
        public GenericClass(string _)
          => ErrorMessage = _;
    }
    

    A solution could be for this problem

    • introduce factory methods
    • change the properties' set to init
    • and create a private ctor
    public class GenericClass<T>
    {
        public T Data { get; init; }
        public string ErrorMessage { get; init; }
    
        public static GenericClass<T> CreateWithData(T _)
          => new GenericClass<T> { Data = _ };
         
        public static GenericClass<string> CreateWithError(string _)
          => new GenericClass<string> { ErrorMessage = _ };
         
        private GenericClass() {}
    }
    

    UPDATE #1 It is not related to the OP's question.

    Last night I have received the Pundit badge and I was a bit surprised.

    I've proposed a solution in a comment then I've left a post with a more detailed description (as the OP asked).

    My comment had only a single upvote at the time when I posted my answer. At the time of this update it has 5 upvotes.

    Why? I mean, why do people prefer to upvote a comment instead of an answer? I think I will never understand how SO members are thinking ...

    Most probably I should do a mind-shift and don't give a damn. I should be happy about the fact the OP got his answer.

    </ventilation_of_my_feelings>