Application of the validity of the chain with the type C # system

One of my biggest complaints about .NET is that there is no way to guarantee that a string matches a certain type in the type system.

Note

This started as a proof of concept rather than a real usable system, but I'm curious about the feasibility of working in the real world now, because it seems moderately usable.

That is, let's say that I want an alphanumeric string, or that it is not longer than a certain length, I have no guarantee that the string that is passed to a function meets those requirements. I have to execute my validation every time I call a function that needs that validity.

This problem is a difficult problem to correct, especially because rope is sealed. Because we can not inherit from a rope, we have to build our own implementation.

As a result, I built a simple implementation that seems to work correctly, but I'm curious about any complexity that I might have overlooked.

I tried to make sensible decisions for the case when certain things are nil, but I am curious about any other suggestion that someone may have for other situations that could not be seen.

Start with the ValidatedString abstract class:

[JsonConverter(typeof(ValidatedStringJsonNetConverter))]



public abstract class ValidatedString
: IComparable, IEnumerable, IEnumerableIComparableIComparable, IEquatable, IEquatable, IXmlSerializable
{
abstract abstract string ErrorRequirement {get; }
Exception protected Exception => new ArgumentException ($ "The value must {ErrorRequirement}");

string public string {get; private set; }
public int Length => String.Length;
this talk published[int index] => Chain[index];

ValidatedString protected () {}
Public ValidatedString (string of characters)
{
Chain = Validate (str);
}

private string Validate (string str) => IsValid (str)? str: throw Exception;

abstract protected bool IsValid (string str);

implicit static public string operator (string ValidatedString) => str? .String;
Public Override bool Equals (object obj) => (String == null && obj == null) || (String? .Equals (obj) ?? false);
public override int GetHashCode () => String? .GetHashCode () ?? 0;
public override string ToString () => String? .ToString ();

int IComparable.CompareTo (object obj) => (String == null && obj == null)? 0: ((IComparable) String)? CompareTo (obj) ?? 0;
IEnumerator IEnumerable.GetEnumerator () => ((IEnumerable) String)? GetEnumerator ();
I Public enumerator GetEnumerator () => ((IEnumerable) Chain? .ToCharArray ()). GetEnumerator ();
public int CompareTo (string other) => (String == null && other == null)? 0: String? .CompareTo (others) ?? other.CompareTo (String);
public int CompareTo (ValidatedString other) => (String == null && other.String == null)? 0: String? .CompareTo (other.String) ?? other.String.CompareTo (String);
public bool Equals (string other) => (String == null && other == null) || (String? .Equals (other) ?? false);
public bool Equals (ValidatedString other) => (String == null && other.String == null) || (String? .Equals (other.String) ?? false);

static public operator bool == (ValidatedString a, ValidatedString b) => a.String == b.String;
public static bool operator! = (ValidatedString a, ValidatedString b) => a.String! = b.String;

Public static int Compare (ValidatedString strA, ValidatedString strB) => string.Compare (strA.String, strB.String);
    [SecuritySafeCritical]
    Public static int Compare (ValidatedString strA, ValidatedString strB, StringComparison, CompareType) => string.Compare (strA.String, strB.String, ComparationType);
Public static int Compare (ValidatedString strA, int indexA, ValidatedString strB, int indexB, int length) => string.Compare (strA.String, indexA, strB.String, indexB, length);
    [SecuritySafeCritical]
    public static int Compare (ValidatedString strA, int indexA, ValidatedString strB, int indexB, int length, StringComparison ComparationType) => string.Compare (strA.String, indexA, strB.String, indexB, length, compareType);

public static int CompareOrdinal (ValidatedString strA, ValidatedString strB) => string.CompareOrdinal (strA.String, strB.String);
    [SecuritySafeCritical]
    public static int CompareOrdinal (ValidatedString strA, int indexA, ValidatedString strB, int indexB, int length) => string.CompareOrdinal (strA.String, indexA, strB.String, indexB, length);

public static bool Equals (ValidatedString a, ValidatedString b) => string.Equals (a.String, b.String);
    [SecuritySafeCritical]
    public static bool Equals (ValidatedString a, ValidatedString b, StringComparison, CompareType) => string.Equals (a.String, b.String, ComparationType);

XmlSchema IXmlSerializable.GetSchema () => null;
void IXmlSerializable.ReadXml (reader of XmlReader)
{
var isEmpty = reader.IsEmptyElement;
reader.Read ();
if (isEmpty) return;
Chain = Validate (reader.Value);
}

void IXmlSerializable.WriteXml (writer XmlWriter)
{
writer.WriteValue (String);
}
}

Here, we do a lot of the main work required. This is the basis of our chain validation: we build the infrastructure to make sure we work consistently.

From there, it's just a matter of building an implementation. I built a second important abstract class: RegexString, which can be supplied with a regular expression to perform the validation:

public abstract class RegexString
: ValidatedString
{
Abstract abstract string RegexValidation {get; }
protected summary bool AllowNull {get; }
Protected override string ErrorRequirement => $ "matches the regular expression: {RegexValidation}";

Private Regex _regex;

Protected RegexString () {}
Public RegexString (string str): base (str) {}

protected void bool IsValid (character string)
{
if (_regex == null) {_regex = new Regex (RegexValidation); };
if (str == null) {return AllowNull; }
returns _regex.IsMatch (str);
}
}

That said, nobody have use the RegexString: it's trivial to build other implementations, like a NonEmptyString:

public class NonEmptyString
: ValidatedString
{
protected bypass string ErrorRequirement => "not be null, empty or blank";

NonEmptyString protected () {}
public NonEmptyString (str string): base (str) {}

protected void bool IsValid (string str) =>! string.IsNullOrWhiteSpace (str);
public static explicit operator NonEmptyString (string str) => new NonEmptyString (str);
}

Now, obviously, there is a point in all this, and I'm getting to that now.

In my situations, I often want to guarantee that certain chains, like a Username or email, they are of a certain format. Previously, to do that, I would have to add many save clauses at the beginning of my function to validate them all. Now, instead, I just change its type:

Public class StringEmail: RegexString
{
Protected override string ErrorRequirement => "be a valid format email @.";
Protected override string RegexValidation => @ "^. + @. +  .. + $";
protected bypass bool AllowNull => false;

StringEmail protected () {}
Public StringEmail (string str): base (str) {}

public static explicit operator StringEmail (string str) => new StringEmail (str);
}

Then I need that kind of string in the class:

Public class test
{
public StringEmail Email {get; set; }
}

This allows me to guarantee that the chain is validated before it is delivered to me. Since there are no conversions, the validation process can not be ignored. Even serialization to / from XML / JSON revalidates the string. (This is why we implemented IXmlSerializable, and why do we have a ValidatedStringJsonNetConverter down.)

public class ValidatedStringJsonNetConverter: JsonConverter
{
void public void WriteJson (JsonWriter writer, object value, JsonSerializer serializer) =>
writer.WriteValue ((value as ValidatedString) .String);

object of public override ReadJson (reader JsonReader, type objectType, object existingValue, JsonSerializer serializer) =>
Activator.CreateInstance (objectType, reader.Value);

public cancellation bool CanConvert (type objectType)
{
#if NETSTANDARD_1_0
try
{
returns Activator.CreateInstance (objectType) is ValidatedString;
}
capture
{
// If we can not make an instance, it's definitely not our type
false return;
}
#plus
returns objectType.IsSubclassOf (typeof (ValidatedString)) || objectType == typeof (ValidatedString);
#determinate if
}
}

Some other basic implementations:

public class StringAlpha
: RegexString
{
Protected cancellation string RegexValidation => "^[a-zA-Z]$
protected bypass string ErrorRequirement => "contains only alphabetic characters (a-z)";
protected bypass bool AllowNull => true;

StringAlpha protected () {}
Public StringAlpha (string str): base (str) {}

public static explicit operator StringAlpha (string str) => new StringAlpha (str);
}
public class StringAlphaNum
: RegexString
{
Protected cancellation string RegexValidation => "^[a-zA-Z0-9]$
protected bypass string ErrorRequirement => "contains only alphabetic (a-z) or numeric (0-9) characters";
protected bypass bool AllowNull => true;

StringAlphaNum protected () {}
Public StringAlphaNum (string str): base (str) {}

public static explicit operator StringAlphaNum (string str) => new StringAlphaNum (str);
}
public class StringHex
: RegexString
{
Protected cancellation string RegexValidation => "^[0-9a-fA-F]$
replacement chain protected ErrorRequirement => "be a hexadecimal number";
protected bypass bool AllowNull => true;

StringHex protected () {}
Public StringHex (string str): base (str) {}

public static explicit operator StringHex (string str) => new StringHex (str);
}
public class StringHexPrefix
: RegexString
{
Protected override string RegexValidation => "^ (0x | & H)?[0-9a-fA-F]$
protected bypass string ErrorRequirement => "be a hexadecimal number (optional prefix 0x or & H)";
protected bypass bool AllowNull => true;

StringHexPrefix protected () {}
Public StringHexPrefix (string str): base (str) {}

public static explicit operator StringHexPrefix (string str) => new StringHexPrefix (str);
}
public class StringNum
: RegexString
{
Protected cancellation string RegexValidation => "^[0-9]$
replacement chain protected ErrorRequirement => "contains only numeric characters (0-9)";
protected bypass bool AllowNull => true;

Protected StringNum () {}
Public StringNum (string str): base (str) {}

public static explicit operator StringNum (string str) => new StringNum (str);
}

And finally, some of the remaining base classes could be constructed from:

String_N public abstract class
: RegexString
{
protected abstract int int MaxLength {get; }
Protected override string RegexValidation => $ "^. {{0, {MaxLength}}} $";
protected bypass string ErrorRequirement => $ "not more than {MaxLength} characters";
protected bypass bool AllowNull => true;

Protected String_N () {}
String_N public (string str): base (str) {}
}
StringN_ public abstract class
: RegexString
{
protected abstract int int MinLength {get; }
Protected override string RegexValidation => $ "^. {{{MinLength},}} $";
protected bypass string ErrorRequirement => $ "must not be less than the characters {MinLength}";
protected bypass bool AllowNull => true;

StringN_ () {} protected
public StringN_ (string str): base (str) {}
}
StringNN public abstract class
: RegexString
{
protected abstract int int MinLength {get; }
protected abstract int int MaxLength {get; }
Protected override string RegexValidation => $ "^. {{{MinLength}, {MaxLength}}} $";
Protected override string ErrorRequirement => $ "between the characters {MinLength} and {MaxLength}";
protected bypass bool AllowNull => true;

StringNN protected () {}
Public StringNN (string str): base (str) {}
}
public abstract class StringWhitelist
: RegexString
{
private chain of const _special = @ "[^$.|?*+()";

    protected abstract char[] White list {get; }
Protected cancellation string RegexValidation => $ "^[{CreateWhitelist(Whitelist)}]$
protected bypass string ErrorRequirement => $ "contains only the characters in the whitelist: {CreateWhitelist (Whitelist)}";
protected bypass bool AllowNull => true;

Protected StringWhitelist () {}
Public StringWhitelist (string str): base (str) {}

public static string CreateWhitelist (char[] White list
{
var result = new StringBuilder (whitelist.Length);

foreach (var c in the whitelist)
{
if (_special.IndexOf (c)> = 0)
{
result.Append ($ @ " {c}");
}
plus
{
result.Append (c);
}
}

return result.ToString ();
}
}
public abstract class StringWhitelist_N
: StringWhitelist
{
protected abstract int int MaxLength {get; }
Protected cancellation string RegexValidation => $ "^[{CreateWhitelist(Whitelist)}]{{0, {MaxLength}}} $ ";
protected bypass string ErrorRequirement => $ "no more than {MaxLength} characters and {base.ErrorRequirement}";

StringWhitelist_N () {} protected
public StringWhitelist_N (string str): base (str) {}
}
public abstract class StringWhitelistN_
: StringWhitelist
{
protected abstract int int MinLength {get; }
Protected cancellation string RegexValidation => $ "^[{CreateWhitelist(Whitelist)}]{{{MinLength},}} $ ";
protected bypass string ErrorRequirement => $ "must not be less than the characters {MinLength} and {base.ErrorRequirement}";

protected StringWhitelistN_ () {}
public StringWhitelistN_ (string str): base (str) {}
}
public abstract class StringWhitelistNN
: StringWhitelist
{
protected abstract int int MinLength {get; }
protected abstract int int MaxLength {get; }
Protected cancellation string RegexValidation => $ "^[{StringWhitelist.CreateWhitelist(Whitelist)}]{{{MinLength}, {MaxLength}}} $ ";
Protected override string ErrorRequirement => $ "between the characters {MinLength} and {MaxLength} and {base.ErrorRequirement}";

protected StringWhitelistNN () {}
public StringWhitelistNN (string str): base (str) {}
}

Any comments are welcome, but especially any comments regarding whether this may be safe or not.

And finally, if you want to see it on GitHub: EBrown8534 / Evbpc.Strings