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
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.