JasonS
JasonS

Reputation: 23868

Constructors with the same argument type

I have a Person object with two constructors - one takes an int (personId), the other a string (logonName). I would like another constructor that takes a string (badgeNumber). I know this can't be done, but seems it might be a common situation. Is there a graceful way of handling this? I suppose this would apply to any overloaded method. Code:

public class Person
{
    public Person() {}

    public Person(int personId)
    {
        this.Load(personId);
    }

    public Person(string logonName)
    {
        this.Load(logonName);
    }

    public Person(string badgeNumber)
    {
        //load logic here...
    }

...etc.

Upvotes: 7

Views: 3808

Answers (13)

Salim
Salim

Reputation: 505

Depending on your business constraints:

public class Person
{
    public string Logon { get; set; } = "";
    public string Badge { get; set; } = "";
    
    public Person(string logon="", string badge="") {}
}
// Use as follow 
Person p1 = new Person(logon:"MylogonName");
Person p2 = new Person(badge:"MyBadge");

Upvotes: 0

Jake Pearson
Jake Pearson

Reputation: 27717

If you are using C# 3.0, you can use Object Initializers:

public Person()
{
}

public string Logon { get; set; }
public string Badge { get; set; }

You would call the constructor like this:

var p1 = new Person { Logon = "Steve" };
var p2 = new Person { Badge = "123" };

Upvotes: 1

Derek Park
Derek Park

Reputation: 46846

You have four options that I can think of, three of which have already been named by others:

  1. Go the factory route, as suggested by several others here. One disadvantage to this is that you can't have consistent naming via overloading (or else you'd have the same problem), so it's superficially less clean. Another, larger, disadvantage is that it precludes the possibility of allocating directly on the stack. Everything will be allocated on the heap if you take this approach.

  2. Custom object wrappers. This is a good approach, and the one I would recommend if you are starting from scratch. If you have a lot of code using, e.g., badges as strings, then rewriting code may make this a non-viable option.

  3. Add an enumeration to the method, specifying how to treat the string. This works, but requires that you rewrite all the existing calls to include the new enumeration (though you can provide a default if desired to avoid some of this).

  4. Add a dummy parameter that is unused to distinguish between the two overloads. e.g. Tack a bool onto the method. This approach is taken by the standard library in a few places, e.g. std::nothrow is a dummy parameter for operator new. The disadvantages of this approach are that it's ugly and that it doesn't scale.

If you already have a large base of existing code, I'd recommend either adding the enumeration (possibly with a default value) or adding the dummy parameter. Neither is beautiful, but both are fairly simple to retrofit.

If you are starting from scratch, or only have a small amount of code, I'd recommend the custom object wrappers.

The factory methods would be an option if you have code which heavily uses the raw badge/logonName strings, but doesn't heavily use the Person class.

Upvotes: 2

Drakiula
Drakiula

Reputation: 327

How about ...

public Person(int personId)
{
    this.Load(personId);
}

public Person(string logonName)
{
    this.Load(logonName);
}

public Person(Object badgeNumber)
{
    //load logic here...
}

Upvotes: -2

Zack Peterson
Zack Peterson

Reputation: 57343

You might consider using custom types.

For example, create LogonName and BadgeNumber classes.

Then your function declarations look like...

public Person(LogonName ln)
{
    this.Load(ln.ToString());
}

public Person(BadgeNumber bn)
{
    //load logic here...
}

Such a solution might give you a good place to keep the business logic that governs the format and usage of these strings.

Upvotes: 7

17 of 26
17 of 26

Reputation: 27382

As has been suggested, custom types is the way to go in this case.

Upvotes: 1

Adam Wright
Adam Wright

Reputation: 49376

You could switch to a factory style pattern.

public class Person {

  private Person() {}

  public static PersonFromID(int personId)
  {
    Person p = new Person().
    person.Load(personID);

    return p;
    this.Load(personId);
  }

  public static PersonFromID(string name)
  {
    Person p = new Person().
    person.LoadFromName(name);

    return p;
  }

  ...
}

Or, as suggested, use custom types. You can also hack something using generics, but I wouldn't recommend it for readability.

Upvotes: 0

Tim Frey
Tim Frey

Reputation: 9941

You could use a static factory method:

public static Person fromLogon(String logon) { return new Person(logon, null); }
public static Person fromBadge(String badge) { return new Person(null, badge); }

Upvotes: 1

Rob Cooper
Rob Cooper

Reputation: 28867

You cannot have two different constructors/methods with the same signature, otherwise, how can the compiler determine which method to run.

As Zack said, I would consider creating an "options" class where you could actually pass the parameters contained in a custom type. This means you can pretty much pass as many parameters as you like, and do what you like with the options, just be careful you dont create a monolithic method that tries to do everything..

Either that, or vote for the factory pattern..

Upvotes: 1

toolkit
toolkit

Reputation: 50237

You could perhaps use factory methods instead?

public static Person fromId(int id) {
    Person p = new Person();
    p.Load(id);
    return p;
}
public static Person fromLogonName(string logonName) {
    Person p = new Person();
    p.Load(logonName);
    return p;
}
public static Person fromBadgeNumber(string badgeNumber) {
    Person p = new Person();
    // load logic
    return p;
}
private Person() {}

Upvotes: 18

Landon
Landon

Reputation: 15686

That won't work. You might consider making a class called BadgeNumber that wraps a string in order to avoid this ambiguity.

Upvotes: 1

Ryan Farley
Ryan Farley

Reputation: 11431

Only thing I can think of to handle what you're wanting to do is to have to params, one that describes the param type (an enum with LogonName, BadgeNumer, etc) and the second is the param value.

Upvotes: 0

Craig
Craig

Reputation: 12012

No.

You might consider a flag field (enum for readability) and then have the constructor use htat to determine what you meant.

Upvotes: 1

Related Questions