tags:

views:

152

answers:

9

Scenario

I have the following two overloads that I need in order to instantiate two different derived objects from the base class.

Issue

The compiler has a problem with this code since the methods are ambiguous - both taking two strings as arguments.

Question

How can I get round this problem so that the following below code compiles?

Code

    public RateInstrument(string ric, string tenor)
    {
        Ric = ric;
        Tenor = tenor;
    }

    public RateInstrument(string ric, string date)
    {
        Ric = ric;
        Date = ConvDate(date);
    }
A: 

You need to give those two constructors different signatures, at the moment they are both

foo(string, string);

Otherwise how are you (let alone the compiler) going to know which method you intended to call?

You could also place these on different classes, or you could change the type(s) passed into the second constructor, for example the following would both be fine as your second method:

public RateInstrument(string ric, string tenor, string date)
public RateInstrument(string ric, DateTime date)

If neither of those work in your situation (for example because tenor and date are mututally exclusive) then you should take another look at the design of your class, and whether or not the second case should be handled in a different class.

Another alternative that I often use is to delay setting things like the date until after the constructor - again it depends on the design of your class.

Kragen
You cannot give the constructor a different name
GvS
@GvS Its not immedaitely obvious from the question that they are constructors, I've edited the answer.
Kragen
+1  A: 

Have two different functions :

RateInstrumentFromDate(blah,date)

RateInstrumentFromTenor(blah,tenor)

Xinxua
+4  A: 

Change the second to acecpt a date. Its right, and its right.

ck
+1  A: 

The method signature (including constructor) is not affected by the parameter name. You'll have to change one of the constructors, maybe:

    public RateInstrument(string ric, string tenor) 
{ 
    Ric = ric; 
    Tenor = tenor; 
} 

public RateInstrument(string ric, TypeOfDate date) 
{ 
    Ric = ric; 
    Date = date; 
} 
sagie
+3  A: 

(I'll assume it really does make sense to accept the date as a string, and that "ric" means something in your domain. It's certainly a general enough issue.)

You can't do this as the two constructors have the same signature, but a nice workaround is to use static factory methods:

private static RateInstrument(string ric, string tenor, string date)
{
    ...
}

public static RateInstrument FromRicAndTenor(string ric, string tenor)
{
    return new RateInstrument(ric, tenor, null);
}

public static RateInstrument FromRicAndDate(string ric, string date)
{
    return new RateInstrument(ric, null, date);
}

Advantages of static construction methods:

  • Doesn't always have to return a new instance (it could apply pooling, etc)
  • Could return null if that was really useful
  • Can do useful work before and after construction more easily than a constructor can

Disadvantages:

  • Looks odd when you're used to calling "new"
  • Inheritance can become trickier (you'd have to at least make the constructor protected, for non-nested derived types)

(Both of these suffer from lack of injectability, compared with instance methods on an actual factory type, of course.)

Jon Skeet
A: 

Create two static methods, that will create the object for you:

static RateInstrument NewRateInstrumentByTenor(string ric, string tenor) {
   return new RateInstrument {
      Ric = ric,
      Tenor = tenor
   }
}
static RateInstrument NewRateInstrumentByDate(string ric, string date) {
   return new RateInstrument {
      Ric = ric,
      Date = ConvDate(date)
   }
}
GvS
A: 

There's a few possible options here. Without seeing a bit more context, I can't really say which is better, but here are some of them.

  1. Change the date parameter from a string to a proper Date object to differentiate the two signatures.

  2. Use the same constructor for both and use something along the lines of DateTime.TryParse to determine if the second argument is a date or tenor.

  3. Use factory methods instead of a constructor so that they may be named differently.

Ideas 1 and 3 assume that the calling code knows something about which type of object you are trying to create. Idea 2 allows the class to remain generic and decide for itself.

Just my 2¢ on the subject.

Chuck
+1  A: 

Why not simply use a constructor that takes 3 arguments ?

public RateInstrument(string ric, string tenor, string date)
{
    Ric = ric;
    if (!string.IsNullOrEmpty(tenor))
    {
            Tenor = tenor;
    }

    if (!string.IsNullOrEmpty(date))
    {
        Date = ConvDate(date);
    }
}
Shimrod
A: 

You can't have two methods that have the same signature. The signature consists of:

  • The name of the method
  • The number of parameters
  • The data types and order of the parameters
  • The parameter modifiers

NOTE: The return type is not part of the signature. Also, it is the types and order of the parameters that are important, not their names.

You might consider changing the data type of the parameter that takes string date to take a DateTime object instead of a string. That will change the signature of the second method.

Ben McCormack
The signature also includes the number of generic type parameters. And also, two methods may not differ solely in out/ref-ness. That is M(ref int) and M(out int) are considered to be a signature collision.
Eric Lippert
@Eric Thanks for the clarification. What would a signature collision based on the number of generic type parameters look like?
Ben McCormack
My point is that void M(int x), void M<T>(int x) and void M<T, U>(int x) are three different signatures even though they have the same return type, name, and formal parameter types.
Eric Lippert
ahh. I didn't realize that. Thanks again.
Ben McCormack