20

也许我是在证明我对 C# 或 .NET 框架的一些常用功能的无知,但我想知道是否有一种本机支持的方式来创建类型别名,例如EmailAddresswhich aliasesstring但这样我可以用它来扩展它我自己的方法喜欢bool Validate()

我知道using x = Some.Type;别名,但它们不是全局的,也不提供类型安全性,即可以将一个普通的别名替换string为当前文件中的 using 别名。我希望 myEmailAddress成为它自己的类型,独立且不能与string它所覆盖的类型互换。

我目前的解决方案是public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable使用 T4 模板生成类,生成样板隐式字符串转换运算符和其他类似的东西。这对我来说很好,并且给了我很大的灵活性,但在我的脑海里,我必须生成如此大量的样板代码来做一些简单的事情,比如创建一个强类型别名,这似乎很愚蠢。

也许除了代码生成之外这是不可能的,但我很好奇其他人是否尝试过类似的设计以及您的经历。如果不出意外,也许这可以作为 C# 的假设未来版本中此类别名功能的一个很好的用例。谢谢!

编辑:我想要的真正价值是能够使用表示不同数据类型/格式的原始类型来获得类型安全。例如, anEmailAddress和 aSocialSecurityNumber和 a PhoneNumber,它们都string用作它们的基础类型,但它们本身不是可互换的类型。我认为这使您的代码更具可读性和自记录性,更不用说更多方法重载可能性的额外好处,这些可能性不那么模棱两可。

4

7 回答 7

5

如果您查看 .NET Framework System.Uri 是最接近的示例,它类似于电子邮件地址。在 .NET 中,模式是将某些东西包装在一个类中并以这种方式添加约束。

添加强类型以向简单类型添加额外的约束是我相信某些函数式语言具有的有趣的语言特性。我不记得语言的名称,它可以让您在值中添加诸如英尺之类的尺寸单位,并对您的方程式进行尺寸分析以确保单位匹配。

于 2009-12-18T00:28:15.293 回答
4

关于为什么string被密封的一些背景:

来自http://www.code-magazine.com/Article.aspx?quickid=0501091

罗里:嘿,杰,你介意我问你几个问题吗?我已经对一些事情很好奇了。首先,这是在我本周做的一个 MSDN 活动中提出的,为什么 String 是密封的?注意:对于 VB.NET 程序员,Sealed = NotInheritable。

Jay:因为我们在 String 中做了很多魔术来尝试确保我们可以针对比较之类的事情进行优化,以尽可能快地进行比较。所以,我们从那里的指针和其他东西中窃取位来标记。举个例子,我刚开始的时候不知道,但是如果字符串中有连字符或撇号[那么]它的排序方式与它只有文本时不同,排序算法如果你有一个连字符或一个撇号,如果你正在做全局感知排序是相当复杂的,所以我们实际上标记了字符串中是否有这种类型的行为。

Rory:所以,你的意思是,在弦乐世界中,如果你不密封弦乐,那么如果人们试图将它子类化,就会有很大的空间造成很大的破坏。

杰:没错。它会改变对象的整个布局,因此我们将无法发挥我们所玩的加快速度的技巧。

这是您可能以前看过的 CodeProject 文章:

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

所以,是的,隐式运算符是您唯一的解决方案。

于 2009-12-18T00:05:03.070 回答
2

该课程是否System.Net.Mail.MailAddress符合您的需求,或者至少是“帮助”?

编辑:它不是明确的 IEquatable 或 ISerializable,但您可以轻松地将它们添加到您自己的包装器中。

于 2009-12-17T23:51:01.217 回答
2

看来您至少有一个合理的 C# knoledgde,所以我的回答可能看起来很愚蠢,但是您想要的是所谓的“类型层次结构”,而编写 String 类的人想要阻止您使用这个“OO 功能”,所以他们制作了 String类密封,这就是为什么你将无法做你想做的事。最好的方法是您现在正在使用的方法:创建自己的类型并隐式转换为 String。

于 2009-12-18T00:29:11.393 回答
1

我想我不明白你为什么要同时拥有强类型和隐式字符串转换。对我来说,一个排除另一个。

我试图为整数解决同样的问题(你在标题中提到了 int,但在问题中没有提到)。我发现声明一个枚举会给你一个类型安全的整数,它需要从/到 int 显式转换。

更新

枚举可能不适用于开放集,但仍然可以这种方式使用。此示例来自编译实验,用于区分数据库中几个表的 ID 列:

    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)
    {
    }
于 2009-12-18T00:15:00.420 回答
1

我认为您想使用扩展方法。它们允许您在不创建新派生类型的情况下扩展类功能。

于 2009-12-18T00:19:30.037 回答
0

我制作了这门课来满足相同的需求。这一个用于“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),请告诉我,我将添加代码。

于 2015-12-08T00:19:13.073 回答