Новый тип на основе строки
От: sergii.p  
Дата: 26.01.23 15:46
Оценка:
хочется создать новый тип на основе строки, чтобы были запрещены операции сравнения, присваивания к строке.
Есть какое-то общепринятое решение? Типа best practice.
У меня что-то много boilerplate кода получается
public struct LanguageCode
{
    public LanguageCode(string v)
    {
       value = v;
    }

    private readonly string value;
    public override bool Equals(object obj)
        => obj is LanguageCode lc
            ? lc.value.Equals(value, System.StringComparison.CurrentCultureIgnoreCase)
            : false;
    public static bool operator ==(LanguageCode fst, LanguageCode snd)
        => fst.value.Equals(snd.value, System.StringComparison.CurrentCultureIgnoreCase);
    public static bool operator !=(LanguageCode fst, LanguageCode snd)
        => !fst.value.Equals(snd.value, System.StringComparison.CurrentCultureIgnoreCase);
    public override int GetHashCode()
        => value.ToLower().GetHashCode();
    public static LanguageCode Default
        => new LanguageCode("en");
    public override string ToString()
        => value.ToLower();
}

Для одиночного такого типа нормально. Но если хочу завести таких ещё парочку? Copy-Paste?
Re: Новый тип на основе строки
От: Слава  
Дата: 26.01.23 16:17
Оценка: 80 (2)
Здравствуйте, sergii.p, Вы писали:

SP>хочется создать новый тип на основе строки, чтобы были запрещены операции сравнения, присваивания к строке.

SP>Для одиночного такого типа нормально. Но если хочу завести таких ещё парочку? Copy-Paste?

Можно и генератор использовать.
Рекомендую всю серию:
https://andrewlock.net/using-strongly-typed-entity-ids-to-avoid-primitive-obsession-part-1/
Re: Новый тип на основе строки
От: _FRED_ Черногория
Дата: 26.01.23 16:29
Оценка: 10 (2)
Здравствуйте, sergii.p, Вы писали:

SP>хочется создать новый тип на основе строки,

OK

SP>чтобы были запрещены операции сравнения, присваивания к строке.


Это как? Можно увидеть пример того, что "разрешено", но нужно запретить?

SP>Есть какое-то общепринятое решение? Типа best practice.

SP>У меня что-то много boilerplate кода получается…
SP>Для одиночного такого типа нормально. Но если хочу завести таких ещё парочку? Copy-Paste?

По идее, если я правильно понял,задачу, код выше можно переписать так вот:
public readonly struct LanguageCode : IEquatable<LanguageCode>
{
  private readonly string value;

  public LanguageCode(string value) => this.value = value;

  public static LanguageCode Default { get; } = new LanguageCode("en");

  public bool Equals(LanguageCode other) => String.Equals(value, other.value, StringComparison.CurrentCultureIgnoreCase);
  public override bool Equals(object? obj) => obj is LanguageCode other && Equals(other);
  public override int GetHashCode() => value?.GetHashCode(StringComparison.CurrentCultureIgnoreCase) ?? 0;

  public override string ToString() => value?.ToLower() ?? String.Empty;

  public static bool operator ==(LanguageCode left, LanguageCode right) => left.Equals(right);
  public static bool operator !=(LanguageCode left, LanguageCode right) => !(left == right);
}

и сделать, например, source generator который будет дописывать за вас boilerplate. Лично для меня, тут проще вручную обойтись, выглядит не слишком плохо.

Можно пойти дальше и использовать record:
public readonly record struct LanguageCode
{
  public LanguageCode(string value) => Value = value?.ToLower() ?? String.Empty;

  public static LanguageCode Default { get; } = new LanguageCode("en");

  private string Value { get; }

  public override string ToString() => Value;
}

Тут компилятор вам сам нагенерит тот же код, который я привёл выше.

Я бы постарался бы сделать так, если я правильно понял Default, чтобы в подобной структурке, будь она мне нужна, выполнялось бы следующее равенство:
default(LanguageCode) == LanguageCode.Default;

Через record этого пока не добиться, остаётся "ручная" реализация сравнения.
  Например, так можно
public readonly struct LanguageCode : IEquatable<LanguageCode>
{
  public const string DefaultLanguageCode = "en";

  private readonly string? value;

  public LanguageCode(string value) => this.value = value;

  public static LanguageCode Default { get; } = default;

  private string Value => value ?? DefaultLanguageCode;

  public bool Equals(LanguageCode other) => String.Equals(Value, other.Value, StringComparison.CurrentCultureIgnoreCase);
  public override bool Equals(object? obj) => obj is LanguageCode other && Equals(other);
  public override int GetHashCode() => Value.GetHashCode(StringComparison.CurrentCultureIgnoreCase);

  public override string ToString() => Value.ToLower();

  public static bool operator ==(LanguageCode left, LanguageCode right) => left.Equals(right);
  public static bool operator !=(LanguageCode left, LanguageCode right) => !(left == right);
}
Help will always be given at Hogwarts to those who ask for it.
Отредактировано 27.01.2023 13:46 _FRED_ (Подкорректировал код рекорда) . Предыдущая версия .
Re: Новый тип на основе строки
От: pilgrim_ Россия  
Дата: 26.01.23 16:38
Оценка: 2 (1)
Здравствуйте, sergii.p, Вы писали:

SP>хочется создать новый тип на основе строки, чтобы были запрещены операции сравнения, присваивания к строке.

SP>Есть какое-то общепринятое решение? Типа best practice.
SP>У меня что-то много boilerplate кода получается

Типизированный id
Автор: pilgrim_
Дата: 23.06.17
Re[2]: Новый тип на основе строки
От: sergii.p  
Дата: 26.01.23 17:16
Оценка: +1
Здравствуйте, _FRED_, Вы писали:

_FR>Можно пойти дальше и использовать record:


прям закидали вкусными предложениями, но это мне кажется наиболее интересным.
Re: Новый тип на основе строки
От: vaa  
Дата: 27.01.23 04:46
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>хочется создать новый тип на основе строки, чтобы были запрещены операции сравнения, присваивания к строке.

SP>Есть какое-то общепринятое решение? Типа best practice.
SP>У меня что-то много boilerplate кода получается
можно такие штуки на F# фигачить(отдельным модулем), будет коротко и смачно
https://fsharpforfunandprofit.com/posts/designing-with-types-more-semantic-types/
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Новый тип на основе строки
От: sergii.p  
Дата: 27.01.23 08:13
Оценка:
Здравствуйте, vaa, Вы писали:

vaa>можно такие штуки на F# фигачить(отдельным модулем), будет коротко и смачно


был бы у меня выбор, я бы такие штуки на Rust писал. Ещё короче. Вообще уже стал сомневаться, что такие вещи стоит реализовывать на C#. Многовато кода, а выигрыш не очевиден.
Re[3]: Новый тип на основе строки
От: vaa  
Дата: 30.01.23 06:03
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, vaa, Вы писали:


vaa>>можно такие штуки на F# фигачить(отдельным модулем), будет коротко и смачно


SP>был бы у меня выбор, я бы такие штуки на Rust писал. Ещё короче. Вообще уже стал сомневаться, что такие вещи стоит реализовывать на C#. Многовато кода, а выигрыш не очевиден.


да сила сишарпа в простоте, прямые проверки на нул и т.п. он ближе по духу к лиспу чем к хаскелю.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Новый тип на основе строки
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.01.23 08:14
Оценка:
Здравствуйте, sergii.p, Вы писали:
SP>был бы у меня выбор, я бы такие штуки на Rust писал. Ещё короче. Вообще уже стал сомневаться, что такие вещи стоит реализовывать на C#. Многовато кода, а выигрыш не очевиден.
С рекордом кода практически минимум. Как бы, чего ещё?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Новый тип на основе строки
От: sergii.p  
Дата: 30.01.23 08:52
Оценка: 78 (1)
Здравствуйте, Sinclair, Вы писали:

S>С рекордом кода практически минимум. Как бы, чего ещё?


лично для меня не взлетело. Оказалось что проект старый — даже рекорды не поддерживает. Менять версию фреймворка не хочу пока.
К тому же надо подружить с EF, а там ещё кода надо генерить.
В общем, прикинул так: не сейчас.
Re[5]: Новый тип на основе строки
От: _FRED_ Черногория
Дата: 30.01.23 11:59
Оценка:
Здравствуйте, sergii.p, Вы писали:

S>>С рекордом кода практически минимум. Как бы, чего ещё?


SP>лично для меня не взлетело. Оказалось что проект старый — даже рекорды не поддерживает. Менять версию фреймворка не хочу пока.

SP>К тому же надо подружить с EF, а там ещё кода надо генерить.
SP>В общем, прикинул так: не сейчас.

Если у вас .NET Framework, то с помощью T4 нагенерировать этого вот хоть для тыщи своих типов совсем не сложно:
_FR>partial struct LanguageCode
_FR>{
_FR>  public override bool Equals(object? obj) => obj is LanguageCode other && Equals(other);

_FR>  public static bool operator ==(LanguageCode left, LanguageCode right) => left.Equals(right);
_FR>  public static bool operator !=(LanguageCode left, LanguageCode right) => !(left == right);
_FR>}

Останется вручную определить только типизированный Equals и GetHashCode().
Help will always be given at Hogwarts to those who ask for it.
Re[5]: Новый тип на основе строки
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 30.01.23 15:25
Оценка:
Здравствуйте, sergii.p, Вы писали:

S>>С рекордом кода практически минимум. Как бы, чего ещё?


SP>лично для меня не взлетело. Оказалось что проект старый — даже рекорды не поддерживает. Менять версию фреймворка не хочу пока.

SP>К тому же надо подружить с EF, а там ещё кода надо генерить.
SP>В общем, прикинул так: не сейчас.

Можно с анонимными классами побаловаться. К ним компилятор тоже генерирует нужные методы.
Можно декомпильнуть и вставить код!
и солнце б утром не вставало, когда бы не было меня
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.