2008-08-27 13 views
7

Tengo un objeto Person con dos constructores: uno toma un int (personId) y el otro una cadena (logonName). Me gustaría otro constructor que tome una cadena (badgeNumber). Sé que esto no se puede hacer, pero parece que podría ser una situación común. ¿Hay una forma elegante de manejar esto? Supongo que esto se aplicaría a cualquier método sobrecargado. Código:Constructores con el mismo tipo de argumento

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.

Respuesta

7

Puede considerar el uso de tipos personalizados.

Por ejemplo, crea las clases LogonName y BadgeNumber.

A continuación, sus declaraciones de funciones parecen ...

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

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

una solución de este tipo podría darle un buen lugar para mantener la lógica de negocio que gobierna el formato y uso de estas cadenas.

1

Usted podría considerar un campo de marca (enumeración para facilitar la lectura) y luego tener el uso del constructor Htat para determinar lo que quería decir.

0

Lo único que se me ocurre para manejar lo que quieres hacer es tener params, uno que describe el tipo de param (una enumeración con LogonName, BadgeNumer, etc.) y el segundo es el valor param.

1

Eso no funcionará. Puede considerar crear una clase llamada BadgeNumber que envuelva una cadena para evitar esta ambigüedad.

18

¿Podría utilizar métodos de fábrica en su lugar?

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() {} 
1

No puede tener dos constructores/métodos diferentes con la misma firma; de lo contrario, ¿cómo puede el compilador determinar qué método ejecutar?

Como Zack said, consideraría crear una clase de "opciones" donde podría pasar los parámetros contenidos en un tipo personalizado. Esto significa que puede pasar la mayor cantidad de parámetros que desee y hacer lo que quiera con las opciones, solo tenga cuidado de no crear un método monolítico que intente hacer todo ...

O eso, o vote por el factory pattern ..

1

Se puede usar un método de fábrica estática:

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

Se podría cambiar a un modelo del estilo de la fábrica.

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; 
    } 

    ... 
} 

O, como se sugiere, utilice tipos personalizados. También puede hackear algo utilizando genéricos, pero no lo recomendaría para la legibilidad.

1

Como se ha sugerido, los tipos personalizados son el camino a seguir en este caso.

-2

¿Qué tal ...

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

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

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

Tienes cuatro opciones que se me ocurre, tres de los cuales ya han sido nombrados por los demás:

  1. ir a la ruta de la fábrica, según lo sugerido por varios otros aquí. Una desventaja de esto es que no puede tener nombres consistentes por sobrecarga (de lo contrario tendría el mismo problema), por lo que es superficialmente menos limpio. Otra desventaja más grande es que excluye la posibilidad de asignar directamente en la pila. Todo se asignará en el montón si toma este enfoque.

  2. Envoltorios de objetos personalizados. Este es un buen enfoque, y el que recomendaría si está empezando desde cero. Si tiene una gran cantidad de código utilizando, por ejemplo, insignias como cadenas, entonces reescribir el código puede hacer que esta sea una opción inviable.

  3. Agregue una enumeración al método, especificando cómo tratar la cadena. Esto funciona, pero requiere que reescriba todas las llamadas existentes para incluir la nueva enumeración (aunque puede proporcionar un valor predeterminado si lo desea para evitar algo de esto).

  4. Agregue un parámetro ficticio que no se use para distinguir entre las dos sobrecargas. p.ej. Tack bool en el método. Este enfoque lo toma la biblioteca estándar en algunos lugares, p. std::nothrow es un parámetro ficticio para operator new. Las desventajas de este enfoque son que es feo y no se escala.

Si ya tiene una gran base de código existente, lo recomiendo, ya sea añadiendo la enumeración (posiblemente con un valor por defecto) o añadiendo el parámetro ficticio. Ninguno de los dos es bello, pero ambos son bastante simples de adaptar.

Si está comenzando desde cero, o solo tiene una pequeña cantidad de código, recomendaría los envoltorios de objetos personalizados.

Los métodos de fábrica serían una opción si tiene un código que usa en gran medida las cadenas badge/logonName sin procesar, pero no usa la clase Person.

2

Si está usando C# 3.0, puede utilizar Object Initializers:

public Person() 
{ 
} 

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

que llamarían el constructor de esta manera:

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

Eso es una sola (una línea) declaración, pero se echa de menos la ventaja de los constructores: los constructores * requieren * que proporcione algunos parámetros, mientras que los inicializadores son solo variables públicas. – drizin

Cuestiones relacionadas