tags:

views:

225

answers:

12

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.

+5  A: 

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.

Zack Peterson
+1  A: 

No.

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

Craig
A: 

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.

Ryan Farley
+1  A: 

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

Landon
+13  A: 

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() {}
toolkit
+1  A: 

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..

Rob Cooper
+1  A: 

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); }
Outlaw Programmer
A: 

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.

Adam Wright
+1  A: 

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

17 of 26
A: 

How about ...

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

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

public Person(Object badgeNumber)
{
    //load logic here...
}
Drakiula
+1  A: 

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.

Derek Park
+1  A: 

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

public Person()
{
}

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

You would call the contructor like this:

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