Monday, March 19, 2018

String theory in C#. My own string!

Well, if Java can do it, so can I! Why not make my own C# string class that can do whatever I want it to do? Requirements? Here they are:
  • I want it to work just like regular strings
  • I want to know if the string is empty just by using it as an if clause, just like Javascript: if (text) doSomething(text);
  • I want to see if it's a number just by adding a plus sign in front of it: var number = +text;
  • I want to easily know if the string is null or whitespace!

OK, so first I want to get all the functionality of a normal string. So I will just inherit from it! Only I can't. String is a sealed class in .Net.
Yet there is an abstract class that seems destined to be used by this: ValueType! It's only implemented by all value types (more or less) except String, but I will be the next guy to use it! Only I can't. If I try, I get the occult message: "Error CS0644 'EString' cannot derive from special class 'ValueType'". But it does help with something, if I try to inherit from it, it tells me what methods to implement: Equals, GetHashCode and ToString.
OK, then, I will just do everything from scratch!

Start with a struct (no need for a class) that wraps a string _value field, then overwrite the equality methods:
public struct EString
{
    private string _value;
 
    public override bool Equals(object obj)
    {
        return String.Equals(obj, _value);
    }
 
    public override int GetHashCode()
    {
        return _value == null ? 0 : _value.GetHashCode();
    }
 
    public override string ToString()
    {
        return _value;
    }
}

I used 0 for the hash code when the value is null because that's what the .Net object GetHashCode() does. Now, in order for this to work as a string, I need some implicit conversion from string to my EString class. So add these two beauties:
public string Value { get => _value; }
 
private EString(string value) { _value = value; }
 
public static implicit operator string(EString estring)
{
    return estring.Value;
}
 
public static implicit operator EString(string value)
{
    return new EString(value);
}

EString now supports stuff like EString text="Something";, but I want more! Let's overload some operators. I want to be able to see if a string is null or empty just like in Javascript:
public static bool operator !(EString estring)
{
    return String.IsNullOrEmpty(estring);
}
 
public static bool operator true(EString estring)
{
    return !!estring;
}
 
public static bool operator false(EString estring)
{
    return !estring;
}
Yes, ladies and gentlemen, true and false are not only boolean constants, but also operators. That doesn't mean something like this is legal: if (text==true) ..., but you can use stuff like if (text) ... or text ? "something" : "empty". Overloading the ! operator allows also something more useful like if (!text) [this shit is empty].

Moar! How about a simple way to know if the string is null or empty or whitespace? Let's overload the ~ operator. How about getting the number encoded into the text? Let's overload the + operator. Here is the final result:
public struct EString
{
    private string _value;
 
    public string Value { get => _value; }
 
    private EString(string value) { _value = value; }
 
    public override bool Equals(object obj)
    {
        return String.Equals(obj, _value);
    }
 
    public override int GetHashCode()
    {
        return _value == null ? 0 : _value.GetHashCode();
    }
 
    public override string ToString()
    {
        return _value;
    }
 
    public static bool operator !(EString estring)
    {
        return String.IsNullOrEmpty(estring);
    }
 
    public static bool operator true(EString estring)
    {
        return !!estring;
    }
 
    public static bool operator false(EString estring)
    {
        return !estring;
    }
 
    public static bool operator ~(EString estring)
    {
        return String.IsNullOrWhiteSpace(estring);
    }
 
    public static decimal operator +(EString estring)
    {
        return decimal.TryParse(estring.Value, out decimal d) ? d : decimal.Zero;
    }
 
    public static implicit operator string(EString estring)
    {
        return estring.Value;
    }
 
    public static implicit operator EString(string value)
    {
        return new EString(value);
    }
}

Disclamer: I did this mostly for fun. I can't condone replacing your strings with my class, but look at these examples for usage:
EString s=inputText;
if (!~s) {
  var d = Math.Round(+s,2);
  if (d != 0) {
    Console.WriteLine("Number was introduced: "+d);
  }
}

0 comments: