В моей системе я часто работаю с кодами аэропортов ( "YYZ"
, "LAX"
, "SFO"
и т.д.), они всегда находятся в том же формате (3 письма, представленный в верхнем регистре). Система обычно имеет дело с 25-50 из этих (разных) кодов на запрос API, с общим количеством распределений более тысячи, они проходят через многие уровни нашего приложения и довольно часто сравниваются на равенство.
Мы начали с простой передачи строк, которые немного подействовали, но мы быстро заметили множество ошибок в программировании, передав неправильный код, где ожидался трехзначный код. Мы также столкнулись с проблемами, при которых мы должны были делать сравнение без учета регистра, а не делать этого, что приводило к ошибкам.
Исходя из этого, я решил прекратить передачу строк и создать Airport
класс, который имеет единственный конструктор, который принимает и проверяет код аэропорта.
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);
}
}
Это сделало наш код намного проще для понимания, и мы упростили наши проверки на равенство, использование словаря / набора. Теперь мы знаем, что если наши методы принимают Airport
экземпляр, который будет вести себя так, как мы ожидаем, он упростит наши проверки методов до проверки нулевых ссылок.
Однако я заметил, что сборка мусора выполнялась гораздо чаще, и я проследил это до многих случаев Airport
сбора.
Мое решение для этого было преобразовать class
в struct
. В основном это было просто изменение ключевого слова, за исключением GetHashCode
и ToString
:
public override string ToString()
{
return Code ?? string.Empty;
}
public override int GetHashCode()
{
return Code?.GetHashCode() ?? 0;
}
Для обработки случая, где default(Airport)
используется.
Мои вопросы:
Было ли создание
Airport
класса или структуры хорошим решением в целом, или я решил неправильную проблему / решил ее неправильно, создав тип? Если это не хорошее решение, что является лучшим решением?Как мое приложение должно обрабатывать случаи, когда
default(Airport)
используется? Тип неdefault(Airport)
имеет смысла для моего приложения, поэтому я занималсяif (airport == default(Airport) { throw ... }
там, где получение экземпляраAirport
(и егоCode
свойства) имеет решающее значение для операции.
Примечания: я рассмотрел вопросы C # / VB struct - как избежать случая с нулевыми значениями по умолчанию, который считается недействительным для данной структуры? , и используйте struct или нет, прежде чем задавать мой вопрос, однако я думаю, что мои вопросы достаточно разные, чтобы гарантировать свой собственный пост.
default(Airport)
проблему - просто запретить экземпляры по умолчанию. Вы можете сделать это, написав конструктор без параметров и бросив InvalidOperationException
или NotImplementedException
в нем.