Nel mio sistema ho spesso operare con codici aeroportuali ( "YYZ"
, "LAX"
, "SFO"
, etc.), sono sempre nello stesso identico formato (3 lettera, rappresentato in maiuscolo). Il sistema in genere gestisce 25-50 di questi (diversi) codici per richiesta API, con oltre un migliaio di allocazioni totali, vengono passati attraverso molti livelli della nostra applicazione e vengono confrontati abbastanza spesso per l'uguaglianza.
Abbiamo iniziato con il semplice passaggio di stringhe, il che ha funzionato bene per un po ', ma abbiamo notato rapidamente molti errori di programmazione passando un codice errato da qualche parte previsto per il codice a 3 cifre. Abbiamo anche riscontrato problemi in cui dovevamo fare un confronto senza distinzione tra maiuscole e minuscole e invece non lo abbiamo fatto, risultando in bug.
Da questo, ho deciso di smettere di passare le stringhe in giro e creare una Airport
classe, che ha un unico costruttore che prende e convalida il codice dell'aeroporto.
public sealed class Airport
{
public Airport(string code)
{
if (code == null)
{
throw new ArgumentNullException(nameof(code));
}
if (code.Length != 3 || !char.IsLetter(code[0])
|| !char.IsLetter(code[1]) || !char.IsLetter(code[2]))
{
throw new ArgumentException(
"Must be a 3 letter airport code.",
nameof(code));
}
Code = code.ToUpperInvariant();
}
public string Code { get; }
public override string ToString()
{
return Code;
}
private bool Equals(Airport other)
{
return string.Equals(Code, other.Code);
}
public override bool Equals(object obj)
{
return obj is Airport airport && Equals(airport);
}
public override int GetHashCode()
{
return Code?.GetHashCode() ?? 0;
}
public static bool operator ==(Airport left, Airport right)
{
return Equals(left, right);
}
public static bool operator !=(Airport left, Airport right)
{
return !Equals(left, right);
}
}
Ciò ha reso il nostro codice molto più semplice da comprendere e abbiamo semplificato i nostri controlli di uguaglianza, il dizionario / gli usi impostati. Ora sappiamo che se i nostri metodi accettano Airport
un'istanza che si comporterà come ci aspettiamo, ha semplificato i nostri controlli di metodo a un controllo di riferimento null.
La cosa che ho notato, tuttavia, era che la garbage collection era in esecuzione molto più spesso, che ho rintracciato in molti casi di Airport
raccolta.
La mia soluzione è stata quella di convertire il file a class
in struct
. Principalmente è stata solo una modifica della parola chiave, ad eccezione di GetHashCode
e ToString
:
public override string ToString()
{
return Code ?? string.Empty;
}
public override int GetHashCode()
{
return Code?.GetHashCode() ?? 0;
}
Per gestire il caso in cui default(Airport)
viene utilizzato.
Le mie domande:
Creare una
Airport
classe o strutturare una buona soluzione in generale o sto risolvendo il problema sbagliato / risolvendolo nel modo sbagliato creando il tipo? Se non è una buona soluzione, qual è una soluzione migliore?In che modo l'applicazione deve gestire le istanze in cui
default(Airport)
viene utilizzata? Un tipo di non hadefault(Airport)
senso per la mia applicazione, quindi l'ho fattoif (airport == default(Airport) { throw ... }
in luoghi in cui ottenere un'istanza diAirport
(e le sueCode
proprietà) è fondamentale per l'operazione.
Note: ho esaminato le domande C # / VB struct: come evitare il caso con valori predefiniti pari a zero, che è considerato non valido per la struttura data? e usa struct o no prima di porre la mia domanda, tuttavia penso che le mie domande siano abbastanza diverse da giustificare il proprio post.
default(Airport)
problema è semplicemente vietando le istanze predefinite. Puoi farlo scrivendo un costruttore senza parametri e lanciandolo InvalidOperationException
o NotImplementedException
al suo interno.