Last active
June 8, 2019 08:58
-
-
Save gongdo/89222c1fc3b71e43f0b13b6f448bb9c0 to your computer and use it in GitHub Desktop.
General purpose string id struct.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Text.RegularExpressions; | |
namespace Alpaca | |
{ | |
/// <summary> | |
/// Represents an identity of a domain object that must be unique in certain system. | |
/// An empty Id should be treated as invalid. You must check if the id was empty. | |
/// It can be treated as a string when it used to outside the system. | |
/// </summary> | |
/// <remarks> | |
/// Unlike Guid, it doesn't provide uniqueness by itself. | |
/// Instead, you must choose uniqueness strategy by yourself. | |
/// This gives you more control what object id should be. | |
/// </remarks> | |
public readonly struct Id : IComparable<Id>, IEquatable<Id> | |
{ | |
private readonly string id; | |
private string Value => id ?? ""; | |
private static readonly Regex invalidCharacters = new Regex(@"[\s\r\n\t\f\v]", RegexOptions.Compiled); | |
public static Id Empty => new Id(); | |
/// <summary> | |
/// Initiate the id with specified value. | |
/// The value must not be null, empty or white-space. | |
/// Also the value must not contain space or escape characters. | |
/// </summary> | |
/// <param name="value"></param> | |
public Id(string value) | |
{ | |
if (string.IsNullOrWhiteSpace(value)) | |
{ | |
throw new ArgumentException($"Id should not be null, empty nor white-space.", nameof(value)); | |
} | |
if (invalidCharacters.IsMatch(value)) | |
{ | |
throw new ArgumentException($"Id should not contain space or escape characters.", nameof(value)); | |
} | |
id = value; | |
} | |
public int CompareTo(Id other) => string.Compare(Value, other.Value); | |
public bool Equals(Id other) => string.Equals(Value, other.Value); | |
public override bool Equals(object obj) => string.Equals(Value, ((Id)obj).Value); | |
public override int GetHashCode() => Value.GetHashCode(); | |
public override string ToString() => Value; | |
static public implicit operator Id(string value) => new Id(value); | |
static public explicit operator string(Id id) => id.Value; | |
static public implicit operator Id(Guid value) => new Id(value.ToString()); | |
public static bool operator ==(Id a, Id b) => a.Equals(b); | |
public static bool operator !=(Id a, Id b) => !a.Equals(b); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Alpaca.Testing; | |
using System; | |
using Xunit; | |
namespace Alpaca.Abstract.Test | |
{ | |
public class IdTest | |
{ | |
[Fact] | |
public void IdShouldProvideEquatable() | |
{ | |
new EqualityTester<Id>("a&", "a&", "a&").Test(); | |
} | |
[Theory] | |
[InlineData(null)] | |
[InlineData("")] | |
[InlineData(" ")] | |
[InlineData(" ")] | |
[InlineData("\t")] | |
[InlineData("\n")] | |
[InlineData(" id-starts-with-space")] | |
[InlineData("id-ends-with-space ")] | |
public void IdHasGuardClause(string value) | |
{ | |
Assert.ThrowsAny<ArgumentException>(() => new Id(value)); | |
} | |
[Fact] | |
public void IdShouldWorkWithEqualOperator() | |
{ | |
var a = new Id("a"); | |
var b = new Id("a"); | |
Assert.True(a == b); | |
Assert.False(a != b); | |
} | |
[Fact] | |
public void IdShouldReturnSameHashCodeForSameString() | |
{ | |
var id = new Id("string"); | |
var str = "string"; | |
Assert.Equal(str.GetHashCode(), id.GetHashCode()); | |
} | |
[Fact] | |
public void DefaultIdShouldBeEmptyString() | |
{ | |
Assert.Equal("", Id.Empty.ToString()); | |
} | |
[Fact] | |
public void IdShouldBeConvertableForString() | |
{ | |
Id id = "string"; | |
string str = (string)id; | |
Assert.Equal("string", id.ToString()); | |
Assert.Equal("string", str); | |
} | |
[Fact] | |
public void IdShouldBeConvertableFromGuid() | |
{ | |
Guid guid = Guid.NewGuid(); | |
Id id = guid; | |
Assert.Equal(guid.ToString(), id.ToString()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment