也许我在证明我对C#或.NET框架的一些常用功能的无知,但我想知道是否有一种本机支持的方法来创建类似别名EmailAddress
的别名string
但我可以使用自己的方法bool Validate()
扩展它?
我知道using x = Some.Type;
别名,但这些别名不是全局的,也不提供类型安全性,即可以为当前文件中的使用别名换出普通的string
。我希望我的EmailAddress
是自己的类型,独立且不能与它所影子的string
类型互换。
我目前的解决方案是使用T4模板生成public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable
类,生成样板隐式字符串转换运算符和其他类似的东西。这对我来说很好,并且给了我很大的灵活性,但在我的脑海里,我必须生成大量的样板代码才能做一些像创建强类型别名一样简单的事情。
除了代码生成之外,这可能是不可能的,但我很好奇其他人是否尝试过与他们的设计类似的东西以及你的经历。如果没有别的,也许这可以作为假设未来版本的C#中的这种别名功能的一个很好的用例。谢谢!
编辑:我想要的真正价值是能够通过表示数据的不同类型/格式的原始类型获得类型安全性。例如,EmailAddress
和SocialSecurityNumber
以及PhoneNumber
,所有这些都使用string
作为其基础类型,但它们本身并不是可互换的类型。我认为这会让你获得更具可读性和自我记录的代码,更不用说更多方法过载可能性的附加好处了。
答案 0 :(得分:5)
如果查看.NET Framework System.Uri是最接近的类似于电子邮件地址的示例。在.NET中,模式是在类中包装某些东西并以这种方式添加约束。
添加强类型,为简单类型添加额外的约束是一个有趣的语言功能,我相信一些功能语言。我无法回想起这种语言的名称,它可以让你在你的数值上添加诸如英尺等尺寸单位,并对你的方程进行维度分析,以确保单位匹配。
答案 1 :(得分:4)
关于string
密封原因的一些背景知识:
来自http://www.code-magazine.com/Article.aspx?quickid=0501091:
罗里:嘿杰伊,你介意我问你一个 几个问题?我已经很好奇了 关于一些事情。首先,和 这是在一个MSDN上提出来的 本周我做的事情,为什么是String 密封?注意:对于VB.NET程序员, 密封= NotInheritable。 杰伊:因为我们做了很多魔术 String中的技巧以尝试确保 我们可以优化像 比较,使它们尽可能快 我们可以。所以,我们偷了 有点指针和其他东西 在那里标记的东西。只为了 举个例子,我不知道 当我开始时,这是一个字符串 其中有连字符或撇号 [然后]它的排序不同于它 只有文字和算法 用于排序,如果你有一个连字符或 如果你正在做撇号 全局感知排序很漂亮 复杂,所以我们实际上是标记 字符串是否具有该字符串 其中的行为类型。 罗瑞:所以,你说的是那个 在字符串世界中,如果你没有 密封字符串会有很多 如果,有很多破坏的空间 人们试图将其子类化。 杰伊:没错。它会改变 整个布局的对象,所以我们 将无法发挥技巧 我们发挥提升速度。
以下是您之前可能已经看过的CodeProject文章:
http://www.codeproject.com/KB/cs/expandSealed.aspx
所以是的,隐式算子是你唯一的解决方案。
答案 2 :(得分:2)
System.Net.Mail.MailAddress
课程是否符合您的需求,或者至少是“帮助”?
编辑:它不是明确的IEquatable或ISerializable,但你可以很容易地在你自己的包装器中添加它们。
答案 3 :(得分:2)
看起来你至少有一个合理的C#knoledgde,所以我的答案可能看起来很愚蠢,但你想要的是“类型层次结构”,编码String类的人想要阻止你使用这个“OO功能”所以他们使String类密封,这就是为什么你不能做你想要的。现在最好的方法是:创建自己的类型并隐式转换为String。
答案 4 :(得分:1)
我想我不明白为什么你想要同时拥有强类型和隐式字符串转换。对我来说,一个人排除了另一个。
我试图为int解决同样的问题(你在标题中提到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)
{
}
答案 5 :(得分:1)
我认为你想使用扩展方法。它们允许您扩展类功能而无需创建新的派生类型。
答案 6 :(得分: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),请告诉我,我会添加代码。