创建自定义字符串类

Mag*_*adh 25 c#

我想创建自己的EMailAddress类,其行为类似于字符串类.

所以我喜欢这样做

private EMailAddress _emailAddress = "Test@Test.com";
Run Code Online (Sandbox Code Playgroud)

代替

private EMailAddress _emailAddress = new EMailAddress("Test@Test.com");
Run Code Online (Sandbox Code Playgroud)

有没有办法实现我想要的,或者我需要使用第二种选择.由于字符串是密封的,我不能使用它,并且=运算符不能被重载所以我没有想法如何解决这个问题....

Tho*_*rin 30

您可以通过隐式转换:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

只有在转换中没有数据丢失时才应使用隐式转换.即便如此,我建议您谨慎使用此功能,因为它会使您的代码更难以阅读.

在此示例中,隐式运算符nullnull传递字符串时返回.正如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;
Run Code Online (Sandbox Code Playgroud)

  • 这有`EmailAddress x = null;`表现不同于`String s = null; EmailAddress x = s;`这很奇怪.最好让隐式运算符在null参数上返回null. (3认同)

Ree*_*sey 9

您可以通过从字符串向自定义类型添加隐式运算符来完成此操作.

class EMailAddress 
{
        // ...other members

        public static implicit operator EMailAddress (string address)
        {
            return new EMailAddress(address);
        }
}
Run Code Online (Sandbox Code Playgroud)

但是,我建议谨慎使用.


Mik*_*zyk 5

我认为这个问题是关于在 C# 应用程序中创建类型安全的更一般问题的一个特定案例。我的示例是两种类型的数据:价格和重量。它们有不同的计量单位,因此永远不要尝试为重量分配价格,反之亦然。两者在封面下都是真正的十进制值。(我忽略了这样一个事实,即可能存在磅到公斤等的转换。)同样的想法可以应用于具有特定类型的字符串,如 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;
    }
};
Run Code Online (Sandbox Code Playgroud)

使用“显式”运算符覆盖,可以使用这些类获得一组更严格的操作。每次从一种类型更改为另一种类型时,您都必须手动区分大小写。例如:

    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
    }
Run Code Online (Sandbox Code Playgroud)

只需通过在代码中将“显式”替换为“隐式”,将“显式”运算符更改为“隐式”运算符,就可以在无需任何额外工作的情况下来回转换为底层 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
    }    
Run Code Online (Sandbox Code Playgroud)

为 String 而不是 Decimal 执行此操作时。我喜欢 Thorarin 关于检查 null 并在转换中将 null 传回的答案。例如

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);
}
Run Code Online (Sandbox Code Playgroud)

要将这些类用作 Dictionary 集合的键,您还需要实现 Equals、GetHashCode、运算符 == 和运算符 !=

为了让这一切变得更容易,我创建了一个可以扩展的 ValueType 类,ValueType 类为除转换运算符之外的所有内容调用基类型。

  • 这个问题最完整的答案,完美,谢谢!您是否有机会将您提到的 ValueType 基类添加到您的答案中? (2认同)