Подтвердить что ты не робот

Есть ли способ определить С# строго типизированные псевдонимы существующих примитивных типов типа `string` или` int`?

Возможно, я демонстрирую свое незнание некоторых часто используемых функций С# или .NET Framework, но я хотел бы знать, есть ли способ, основанный на принципе создания псевдонима типа EmailAddress, который псевдонимы string но такое, что я могу расширить его своими собственными методами вроде bool Validate()?

Я знаю псевдонимы using x = Some.Type;, но они не являются глобальными и не обеспечивают безопасность типа, т.е. можно заменить обычную string для псевдонима использования в текущем файле. Я бы хотел, чтобы мой EmailAddress был его собственным типом, независимым и не взаимозаменяемым с типом string, который он тениет.

Мое текущее решение состоит в том, чтобы генерировать классы public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable с шаблоном T4, генерирующим операторы преобразования неявных строк шаблона и другие подобные вещи. На данный момент это хорошо для меня и дает мне большую гибкость, но в глубине моего сознания кажется глупым, что я должен генерировать такое обильное количество кода шаблона, чтобы сделать что-то так же просто, как создать сильный псевдоним типа.

Возможно, это невозможно, кроме как с генерацией кода, но мне любопытно, если другие пытались что-то подобное с их дизайнами и какими были ваши впечатления. Если ничего другого, возможно, это может послужить хорошим вариантом использования для такой функции псевдонима в гипотетической будущей версии С#. Спасибо!

EDIT: реальное значение, которое я хочу из этого, состоит в том, чтобы иметь возможность получить безопасность типов с примитивными типами, которые представляют разные типы/форматы данных. Например, EmailAddress и SocialSecurityNumber и PhoneNumber, все из которых используют string как их базовый тип, но не являются взаимозаменяемыми типами сами по себе. Я думаю, что это дает вам гораздо более читаемый и самодокументирующий код, не говоря уже о преимуществах более сложных возможностей перегрузки методов, которые менее двусмысленны.

4b9b3361

Ответ 1

Если вы посмотрите на .NET Framework System.Uri, это самый близкий пример, похожий на адрес электронной почты. В .NET шаблон должен обернуть что-то в класс и добавить ограничения таким образом.

Добавление сильной типизации, которая добавляет дополнительные ограничения для простых типов, - интересная языковая функция, которая, как мне кажется, имеет некоторый функциональный язык. Я не могу вспомнить название языка, которое позволит вам добавлять размерные единицы, такие как ноги, к вашим значениям и выполнять анализ размеров в ваших уравнениях, чтобы гарантировать соответствие единиц.

Ответ 2

Некоторые предпосылки о том, почему string запечатываются:

От http://www.code-magazine.com/Article.aspx?quickid=0501091:

Рори: Эй, Джей, ты не возражаешь, если я попрошу тебя пару вопросов? Мне уже любопытно о некоторых вещах. Прежде всего, и это было поднято в одном из MSDN события, которые я сделал на этой неделе, почему String запечатаны? Примечание: для программистов VB.NET, Запечатанный = NotInheritable.

Джей: Потому что мы делаем много волшебства трюки в String, чтобы попытаться убедиться мы можем оптимизировать такие вещи, как сравнений, чтобы сделать их так же быстро, как мы, возможно, можем. Итак, мы крадем бит от указателей и других вещей там, чтобы отметить вещи. Просто дайте вам пример, и я не знал это когда я начал, но если строка имеет дефис или апостроф в нем [тогда] он выглядит иначе, чем если бы он просто есть текст в нем, а алгоритм для сортировки, если у вас есть дефис или апостроф, если вы делаете глобально-сортировка сортировки довольно сложный, поэтому мы фактически отмечаем независимо от того, имеет ли строка тип поведения в нем.

Рори: Итак, вы говорите, что в мире струн, если вы не печать String было бы много места для разрушения большого количества хаоса, если люди пытались подклассифицировать его.

Джей: Точно. Это изменило бы весь макет объекта, поэтому мы не смогли бы сыграть трюки что мы играем с этой скоростью.

Вот статья CodeProject, которую вы, вероятно, видели раньше:

http://www.codeproject.com/KB/cs/expandSealed.aspx

Итак, неявный оператор - ваше единственное решение.

Ответ 3

Соответствует ли класс System.Net.Mail.MailAddress вашим потребностям или, по крайней мере, "помогает"?

EDIT: он явно не IEquatable или ISerializable, но вы можете легко добавить их в свою собственную оболочку.

Ответ 4

Кажется, у вас есть хотя бы разумный С# knoledgde, поэтому мой ответ может показаться глупым, но то, что вы хотите, называется "иерархией типов", а ребята, которые кодировали класс String, хотели помешать вам использовать эту функцию "OO", поэтому они сделали класс String запечатанным, поэтому вы не сможете делать то, что хотите. Лучший подход - это вы сейчас: сделайте свой собственный тип и неявное преобразование в String.

Ответ 5

Я думаю, я не понимаю, почему вы хотите иметь как сильные типы, так и неявное преобразование строк одновременно. Для меня один исключает другого.

Я попытался решить ту же проблему для ints (вы упоминаете int в заголовке, но не в вопросе). Я обнаружил, что объявление перечисления дает вам целочисленное целое число, которое должно быть явно отображено из/в int.

Update

Перечисления не могут быть предназначены для открытых наборов, но все равно могут использоваться таким образом. Этот пример из эксперимента по компиляции, чтобы различать столбцы идентификаторов нескольких таблиц в базе данных:

    enum ProcID { Unassigned = 0 }
    enum TenderID { Unassigned = 0 }

    void Test()
    {
        ProcID p = 0;
        TenderID t = 0; <-- 0 is assignable to every enum
        p = (ProcID)3;  <-- need to explicitly convert

        if (p == t)  <-- operator == cannot be applied
            t = -1;  <-- cannot implicitly convert

        DoProc(p);
        DoProc(t);   <-- no overloaded method found
        DoTender(t);
    }

    void DoProc(ProcID p)
    {
    }

    void DoTender(TenderID t)
    {
    }

Ответ 6

Я думаю, вы хотите использовать методы расширения. Они позволяют расширять функциональность классов без создания нового производного типа.

Ответ 7

Я сделал этот класс для удовлетворения одинаковых потребностей. Это для типа "int" (у меня также есть один для "string" ):

public class NamedInt : IComparable<int>, IEquatable<int>
{
    internal int Value { get; }

    protected NamedInt() { }
    protected NamedInt(int val) { Value = val; }
    protected NamedInt(string val) { Value = Convert.ToInt32(val); }

    public static implicit operator int (NamedInt val) { return val.Value; }

    public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
    public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
    public static bool operator !=(NamedInt a, int b) { return !(a==b); }
    public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }

    public bool Equals(int other) { return Equals(new NamedInt(other)); }
    public override bool Equals(object other) {
        if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
        return Equals(new NamedInt(other.ToString()));
    }
    private bool Equals(NamedInt other) {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(Value, other.Value);
    }

    public int CompareTo(int other) { return Value - other; }
    public int CompareTo(NamedInt other) { return Value - other.Value; }

    public override int GetHashCode() { return Value.GetHashCode(); }

    public override string ToString() { return Value.ToString(); }
}

И использовать его в вашем случае:

public class MyStronglyTypedInt: NamedInt {
    public MyStronglyTypedInt(int value) : base(value) {
        // Your validation can go here
    }
    public static implicit operator MyStronglyTypedInt(int value) { 
        return new MyStronglyTypedInt(value);
    }

    public bool Validate() {
        // Your validation can go here
    }
}

Если вам нужно его сериализовать (Newtonsoft.Json), дайте мне знать, и я добавлю код.