Domanda

Ho un oggetto Person con due costruttori: uno accetta un int (personId), l'altro una stringa (logonName).Vorrei un altro costruttore che accetta una stringa (badgeNumber).So che non è possibile farlo, ma sembra che potrebbe essere una situazione comune.Esiste un modo grazioso per gestire la situazione?Suppongo che questo si applichi a qualsiasi metodo sovraccaricato.Codice:

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

...eccetera.

È stato utile?

Soluzione

Potresti prendere in considerazione l'utilizzo di tipi personalizzati.

Ad esempio, crea le classi LogonName e BadgeNumber.

Quindi le tue dichiarazioni di funzione assomigliano a...

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

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

Una soluzione di questo tipo potrebbe offrire un buon posto in cui conservare la logica aziendale che governa il formato e l'utilizzo di queste stringhe.

Altri suggerimenti

Potresti forse usare invece i metodi di fabbrica?

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

Mi vengono in mente quattro opzioni, tre delle quali sono già state nominate da altri:

  1. Segui il percorso della fabbrica, come suggerito da molti altri qui.Uno svantaggio è che non è possibile avere una denominazione coerente tramite sovraccarico (altrimenti si avrebbe lo stesso problema), quindi è superficialmente meno pulito.Un altro svantaggio, più grande, è che preclude la possibilità di allocare direttamente sullo stack.Se si adotta questo approccio, tutto verrà allocato nell'heap.

  2. Wrapper di oggetti personalizzati.Questo è un buon approccio e quello che consiglierei se stai iniziando da zero.Se hai molto codice che utilizza, ad esempio, badge come stringhe, la riscrittura del codice potrebbe rendere questa opzione non praticabile.

  3. Aggiungi un'enumerazione al metodo, specificando come trattare la stringa.Funziona, ma richiede la riscrittura di tutte le chiamate esistenti per includere la nuova enumerazione (sebbene sia possibile fornire un valore predefinito se lo si desidera per evitare tutto ciò).

  4. Aggiungere un parametro fittizio inutilizzato per distinguere tra i due sovraccarichi.per esempio.Virare a bool sul metodo.Questo approccio è adottato dalla libreria standard in alcuni posti, ad es. std::nothrow è un parametro fittizio per operator new.Gli svantaggi di questo approccio sono che è brutto e non è scalabile.

Se disponi già di un'ampia base di codice esistente, ti consiglio di aggiungere l'enumerazione (possibilmente con un valore predefinito) o di aggiungere il parametro fittizio.Nessuno dei due è bello, ma entrambi sono abbastanza semplici da adattare.

Se stai iniziando da zero o hai solo una piccola quantità di codice, ti consiglio i wrapper di oggetti personalizzati.

I metodi factory sarebbero un'opzione se hai codice che utilizza pesantemente il raw badge/logonName stringhe, ma non utilizza pesantemente il file Person classe.

Se si utilizza C# 3.0, è possibile utilizzare Inizializzatori di oggetti:

public Person()
{
}

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

Chiameresti il ​​costruttore in questo modo:

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

NO.

Potresti considerare un campo flag (enum per leggibilità) e quindi chiedere al costruttore di utilizzare htat per determinare cosa intendi.

Non funzionerà.Potresti prendere in considerazione la creazione di una classe chiamata BadgeNumber che avvolga una stringa per evitare questa ambiguità.

Non è possibile avere due costruttori/metodi diversi con la stessa firma, altrimenti come può il compilatore determinare quale metodo eseguire.

COME Ha detto Zack, prenderei in considerazione la creazione di una classe "opzioni" in cui potresti effettivamente passare i parametri contenuti in un tipo personalizzato.Ciò significa che puoi praticamente passare tutti i parametri che desideri e fare ciò che desideri con le opzioni, fai solo attenzione a non creare un metodo monolitico che tenta di fare tutto.

O quello, oppure vota per il modello di fabbrica..

Potresti utilizzare un metodo di fabbrica statico:

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

Come è stato suggerito, i tipi personalizzati sono la strada da percorrere in questo caso.

L'unica cosa che mi viene in mente per gestire ciò che vuoi fare è avere dei param, uno che descriva il tipo di param (un'enumerazione con LogonName, BadgeNumer, ecc.) e il secondo è il valore del param.

Potresti passare a un modello in stile fabbrica.

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

  ...
}

Oppure, come suggerito, utilizza tipi personalizzati.Puoi anche hackerare qualcosa usando i generici, ma non lo consiglierei per la leggibilità.

Che ne dite di ...

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

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

public Person(Object badgeNumber)
{
    //load logic here...
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top