C# creating a non-nullable string. Is it possible? Somehow?

后端 未结 8 900
感情败类
感情败类 2021-01-11 10:05

So you can\'t inherit string. You can\'t make a non-nullable string. But I want to do this. I want a class, let\'s call it nString that returns a d

8条回答
  •  借酒劲吻你
    2021-01-11 10:44

    You are on the right track because you can create a value type (struct) to wrap a .NET primitive type and add some rules around the type without adding any real overhead.

    The only problem is that value types can be default initialized exactly as a string can be default initialized. So you cannot avoid that there exists an "invalid" or "empty" or "null" value.

    Here is a class that wraps a string with the added rule that the string cannot be null or empty. For lack of better name I decided to call it Text:

    struct Text : IEquatable {
    
      readonly String value;
    
      public Text(String value) {
        if (!IsValid(value))
          throw new ArgumentException("value");
        this.value = value;
      }
    
      public static implicit operator Text(String value) {
        return new Text(value);
      }
    
      public static implicit operator String(Text text) {
        return text.value;
      }
    
      public static Boolean operator ==(Text a, Text b) {
        return a.Equals(b);
      }
    
      public static Boolean operator !=(Text a, Text b) {
        return !(a == b);
      }
    
      public Boolean Equals(Text other) {
        return Equals(this.value, other.value);
      }
    
      public override Boolean Equals(Object obj) {
        if (obj == null || obj.GetType() != typeof(Text))
          return false;
        return Equals((Text) obj);
      }
    
      public override Int32 GetHashCode() {
        return this.value != null ? this.value.GetHashCode() : String.Empty.GetHashCode();
      }
    
      public override String ToString() {
        return this.value != null ? this.value : "N/A";
      }
    
      public static Boolean IsValid(String value) {
        return !String.IsNullOrEmpty(value);
      }
    
      public static readonly Text Empty = new Text();
    
    }
    

    You do not have to implement the IEquatable interface but it is a nice addition because you have to override Equals anyway.

    I decided to create two implicit cast operators so this type can be used interchangeably with normal strings. However, implicit cast can be a bit subtle so you might decide to change one or both into explicit cast operators. If you decide to use implicit casts you should probably also override the == and != operator to avoid using the == operator for strings when you really want to use Equals for this type.

    You can use the class like this:

    var text1 = new Text("Alpha");
    Text text2 = "Beta"; // Implicit cast.
    var text3 = (Text) "Gamma"; // Explicit cast.
    var text4 = new Text(""); // Throws exception.
    
    var s1 = (String) text1; // Explicit cast.
    String s2 = text2; // Implicit cast.
    

    However, you still have a "null" or "empty" value:

    var empty = new Text();
    Console.WriteLine(Equals(text, Text.Empty)); // Prints "True".
    Console.WriteLine(Text.Empty); // Prints "N/A".
    

    This concept can easily be extended to more complex "strings", e.g. phone numbers or other strings with a structure. This will allow you to write code that is easier to understand. E.g., instead of

    public void AddCustomer(String name, String phone) { ... }
    

    you can change it to

    public void AddCustomer(String name, PhoneNumber phone) { ... }
    

    The second function does not need to validate the phone number because it already is a PhoneNumber that has to be valid. Compare that to a string that can have any content and in each call you have to validate it. Even though that most seasoned developers probably will agree that it is a bad practice to use strings for string like values like social security numbers, phone numbers, country codes, currencies etc. it seems to be a very common approach.

    Note that this approach does not have any overhead in terms of heap allocations. This is simply a string with some extra validation code.

提交回复
热议问题