我想创建自己的EMailAddress类,其作用类似于字符串类。
所以我喜欢这样做
private EMailAddress _emailAddress = "Test@Test.com";
而不是
private EMailAddress _emailAddress = new EMailAddress("Test@Test.com");
有没有办法实现我想要的,或者我需要使用第二种选择。由于字符串是密封的,我不能使用它,并且=运算符不能重载,所以我没有想法如何解决这个....
答案 0 :(得分:29)
您可以通过implicit转化:
public class EMailAddress
{
private string _address;
public EMailAddress(string address)
{
_address = address;
}
public static implicit operator EMailAddress(string address)
{
// While not technically a requirement; see below why this is done.
if (address == null)
return null;
return new EMailAddress(address);
}
}
只有在转换中没有数据丢失时才应使用隐式转换。即便如此,我建议您谨慎使用此功能,因为它可能会使您的代码更难以阅读。
在此示例中,隐式运算符在传递null
字符串时返回null
。正如Jon Hanna正确评论的那样,让这两段代码表现不同是不可取的:
// Will assign null to the reference
EMailAddress x = null;
// Would create an EMailAddress object containing a null string
string s = null;
EMailAddress y = s;
答案 1 :(得分:8)
您可以通过向自定义类型添加implicit operator from string来执行此操作。
class EMailAddress
{
// ...other members
public static implicit operator EMailAddress (string address)
{
return new EMailAddress(address);
}
}
但是,我建议谨慎使用。
答案 2 :(得分:4)
我认为这个问题是关于在C#应用程序中创建类型安全性的更一般问题的特定情况。我的例子是两种类型的数据:价格和权重。它们具有不同的度量单位,因此不应该尝试为权重分配价格,反之亦然。两个封面下都是十进制值。 (我忽略了这样一个事实,即可以转换成磅到kg等。)同样的想法可以应用于具有特定类型的字符串,如EmailAddress和UserLastName。
使用一些相当的锅炉板代码,可以在特定类型之间来回进行显式转换或隐式转换:价格和权重,以及基础类型十进制。
public class Weight
{
private readonly Decimal _value;
public Weight(Decimal value)
{
_value = value;
}
public static explicit operator Weight(Decimal value)
{
return new Weight(value);
}
public static explicit operator Decimal(Weight value)
{
return value._value;
}
};
public class Price {
private readonly Decimal _value;
public Price(Decimal value) {
_value = value;
}
public static explicit operator Price(Decimal value) {
return new Price(value);
}
public static explicit operator Decimal(Price value)
{
return value._value;
}
};
使用"显式"运算符覆盖,人们可以使用这些类来获得更具限制性的一组事物。每次从一种类型更改为另一种类型时,您必须手动填写。例如:
public void NeedsPrice(Price aPrice)
{
}
public void NeedsWeight(Weight aWeight)
{
}
public void NeedsDecimal(Decimal aDecimal)
{
}
public void ExplicitTest()
{
Price aPrice = (Price)1.23m;
Decimal aDecimal = 3.4m;
Weight aWeight = (Weight)132.0m;
// ok
aPrice = (Price)aDecimal;
aDecimal = (Decimal)aPrice;
// Errors need explicit case
aPrice = aDecimal;
aDecimal = aPrice;
//ok
aWeight = (Weight)aDecimal;
aDecimal = (Decimal) aWeight;
// Errors need explicit cast
aWeight = aDecimal;
aDecimal = aWeight;
// Errors (no such conversion exists)
aPrice = (Price)aWeight;
aWeight = (Weight)aPrice;
// Ok, but why would you ever do this.
aPrice = (Price)(Decimal)aWeight;
aWeight = (Weight)(Decimal)aPrice;
NeedsPrice(aPrice); //ok
NeedsDecimal(aPrice); //error
NeedsWeight(aPrice); //error
NeedsPrice(aDecimal); //error
NeedsDecimal(aDecimal); //ok
NeedsWeight(aDecimal); //error
NeedsPrice(aWeight); //error
NeedsDecimal(aWeight); //error
NeedsWeight(aWeight); //ok
}
只需更改"显式"运营商"隐含"运营商通过替换单词" explicit"用"含义"在代码中,可以在没有任何额外工作的情况下来回转换到底层的Decimal类。这使得“价格”和“重量”的行为更像“十进制”,但您仍然无法将“价格”更改为“权重”。这通常是我正在寻找的类型安全级别。
public void ImplicitTest()
{
Price aPrice = 1.23m;
Decimal aDecimal = 3.4m;
Weight aWeight = 132.0m;
// ok implicit cast
aPrice = aDecimal;
aDecimal = aPrice;
// ok implicit cast
aWeight = aDecimal;
aDecimal = aWeight;
// Errors
aPrice = aWeight;
aWeight = aPrice;
NeedsPrice(aPrice); //ok
NeedsDecimal(aPrice); //ok
NeedsWeight(aPrice); //error
NeedsPrice(aDecimal); //ok
NeedsDecimal(aDecimal); //ok
NeedsWeight(aDecimal); //ok
NeedsPrice(aWeight); //error
NeedsDecimal(aWeight); //ok
NeedsWeight(aWeight); //ok
}
为String而不是Decimal执行此操作时。我喜欢Thorarin关于检查null并在转换中传回null的回答。 e.g。
public static implicit operator EMailAddress(string address)
{
// Make
// EmailAddress myvar=null
// and
// string aNullString = null;
// EmailAddress myvar = aNullString;
// give the same result.
if (address == null)
return null;
return new EMailAddress(address);
}
要使这些类作为Dictionary集合的键,您还需要实现Equals,GetHashCode,operator ==和operator!=
为了使所有这些更容易,我创建了一个可以扩展的ValueType类,ValueType类调用除转换运算符之外的所有内容的基类型。
答案 3 :(得分:3)
示例:
public class EmailAddress
{
private string _value;
private static bool IsValidAddress(string address)
{
//whether to match RFC822 production or have something simpler,
//but excluding valid but unrealistic addresses, is an impl. choice
//out of scope.
return true;
}
public EMailAddress(string value)
{
if(value == null)
throw new ArgumentNullException();
if(!IsValidAddress(value))
throw new ArgumentException();
_value = value;
}
public EmailAddress(Uri uri)
{
if(value == null)
throw new ArgumentNullException();
if(!uri.Scheme != "mailto")
throw new ArgumentException();
string extracted = uri.UserInfo + "@" + uri.Host;
if(!IsValidAddress(extracted))
throw new ArgumentException();
_value = extracted;
}
public override string ToString()
{
return _value;
}
public static implicit operator EMailAddress(string value)
{
return value == null ? null : new EMailAddress(value);
}
public static implicit operator EMailAddress(Uri uri)
{
return value == null ? null : new EMailAddress(uri);
}
}
答案 4 :(得分:0)
正如您所指出的,您无法从字符串继承,但可以使用扩展方法对其进行扩展。
因此,您可以制作一些特定于电子邮件的扩展方法来处理您想要放入EmailAddress类的任何内容。
答案 5 :(得分:0)
我想我知道你要做什么。但是你不能在.NET中继承string
。有时我希望我可以继承int
这样的内置类型,但当然这也是不可能的(尽管出于其他技术原因)。
虽然你理论上可以使用implicit operator
转换方法,正如其他人所建议的那样,但是存在一些问题,例如奇怪的类型转换编译器错误。这个解决方案对我来说效果不佳。
在类似情况下您通常会在BCL中看到的内容 - 即,当某个特定类型应该“感觉”类似于它所基于的类型时 - 是具有 Value
属性的自定义类型。 (我想到的两个例子是System.Nullable<T>
,System.Lazy<T>
。)它并不优雅,但却有效。
答案 6 :(得分:0)
作为@Thorarin 代码的后续:
您可以将其用作基本代码并添加普通字符串类将具有的额外功能:
public class VarChar
{
private string _content;
public VarChar(string argContent)
{
_content = argContent;
}
public static implicit operator VarChar(string argContent)
{
// While not technically a requirement; see below why this is done.
if (argContent == null)
return null;
return new VarChar(argContent);
}
public static implicit operator string(VarChar x) => x._content;
public override string ToString()
{
return _content;
}
}
所以下面的代码就行了:
VarChar tempVarChar = "test";
string tempTest = tempVarChar; // = "test"
var tempText = tempVarChar.ToString();
tempVarChar = tempVarChar + "_oke";
tempText = tempVarChar; // = "test_oke"