Настройка
У меня есть прототип класса TypedString<T>
, который пытается "сильно напечатать" (сомнительное значение) строки определенной категории. Он использует С# -анализ любопытно повторяющегося шаблона шаблона (CRTP).
class TypedString<T>
public abstract class TypedString<T>
: IComparable<T>
, IEquatable<T>
where T : TypedString<T>
{
public string Value { get; private set; }
protected virtual StringComparison ComparisonType
{
get { return StringComparison.Ordinal; }
}
protected TypedString(string value)
{
if (value == null)
throw new ArgumentNullException("value");
this.Value = Parse(value);
}
//May throw FormatException
protected virtual string Parse(string value)
{
return value;
}
public int CompareTo(T other)
{
return string.Compare(this.Value, other.Value, ComparisonType);
}
public bool Equals(T other)
{
return string.Equals(this.Value, other.Value, ComparisonType);
}
public override bool Equals(object obj)
{
return obj is T && Equals(obj as T);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return Value;
}
}
Теперь класс TypedString<T>
может использоваться для устранения дублирования кода при определении группы разных "категорий строк" во всем моем проекте. Пример простого использования этого класса заключается в определении класса Username
:
class Username
(пример)
public class Username : TypedString<Username>
{
public Username(string value)
: base(value)
{
}
protected override string Parse(string value)
{
if (!value.Any())
throw new FormatException("Username must contain at least one character.");
if (!value.All(char.IsLetterOrDigit))
throw new FormatException("Username may only contain letters and digits.");
return value;
}
}
Теперь я могу использовать класс Username
во всем моем проекте, никогда не проверяя правильность форматирования имени пользователя - если у меня есть выражение или переменная типа Username
, > для правильной (или нулевой).
Сценарий 1
string GetUserRootDirectory(Username user)
{
if (user == null)
throw new ArgumentNullException("user");
return Path.Combine(UsersDirectory, user.ToString());
}
Мне не нужно беспокоиться о форматировании пользовательской строки здесь - я уже знаю, что это правильно по характеру типа.
Сценарий 2
IEnumerable<Username> GetFriends(Username user)
{
//...
}
Здесь вызывающий абонент знает, что он получает в качестве возврата только на основе типа. Для параметра IEnumerable<string>
потребуется прочитать подробные сведения о методе или документации. Хуже того, если кто-то изменил реализацию GetFriends
таким образом, чтобы он вводил ошибку и выдавал неверные строки имени пользователя, эта ошибка могла бы бесшумно распространяться среди вызывающих пользователей метода и приводить к разным видам хаоса. Эта красиво типизированная версия предотвращает это.
Сценарий 3
System.Uri
является примером класса в .NET, который делает немного больше, чем перенос строки, которая имеет огромное количество ограничений форматирования и вспомогательных свойств/методов для доступа к полезным частям. Так что одна часть доказательств того, что этот подход не совсем сумасшедший.
Вопрос
Я предполагаю, что это было сделано раньше. Я уже вижу преимущества этого подхода и больше не нуждаюсь в том, чтобы убедить себя.
Есть ли недостаток, который я могу потерять?
Есть ли способ, чтобы это могло вернуться, чтобы укусить меня позже?