Created
August 13, 2018 14:20
-
-
Save eouw0o83hf/2581f240af679ddd4870ad9f1642771e to your computer and use it in GitHub Desktop.
Change: A C# handler for null-safe change handling
This file contains 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 Newtonsoft.Json; | |
namespace eouw0o83hf.Gist.Change | |
{ | |
/// <summary> | |
/// A lightweight wrapper which tracks a change to a property | |
/// and serializes to one json property for ease of reading in | |
/// an event stream. | |
/// </summary> | |
/// <remarks> | |
/// How this only serializes to one field - it needs to be broken | |
/// down by use case for struct Ts (non-nullable) and class Ts | |
/// (nullable): | |
/// | |
/// Struct: | |
/// If a change occurs, Value **will have a value** and Unset will | |
/// be false, which returns null (and JSON serializes to nothing). | |
/// If no change occurs, this entire object will be null. | |
/// | |
/// Class: | |
/// If a change occurs: | |
/// If Value is null, Unset should be set to true. In this | |
/// case, the serializer will ignore Value and only render | |
/// the Unset property. | |
/// If Value has a value, Unset will be false, which returns | |
/// null, and JSON serializes it to nothing. | |
/// If no change occurs, this entire object will be null. | |
/// </remarks> | |
public class Change<T> | |
{ | |
[JsonIgnore] | |
private T _value; | |
[JsonProperty("value")] | |
public T Value | |
{ | |
get => _value; | |
set | |
{ | |
_value = value; | |
_unset = false; | |
} | |
} | |
[JsonIgnore] | |
private bool _unset; | |
/// <summary> | |
/// Denotes that the target property should be set to null. | |
/// Returns true or null to make serialization prettier. | |
/// </summary> | |
[JsonProperty("unset")] | |
public bool? Unset | |
{ | |
get => _unset ? true : (bool?)null; | |
set => _unset = value ?? false; | |
} | |
// Make model binding easy by directly binding given values | |
// of T to Change<T>s which wrap them | |
public static implicit operator Change<T>(T value) => new Change<T> { Value = value }; | |
// I don't think we need this | |
// public static explicit operator T(Change<T> value) => value.Value; | |
// Make it easy to apply a Change<T> to a model. Use: | |
// myProperty |= myPropertyChange; | |
public static T operator |(T left, Change<T> right) | |
=> right != null ? right.Value : left; | |
} | |
} |
This file contains 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 Fermion.Web.Events; | |
using Xunit; | |
namespace eouw0o83hf.Gist.Tests.Change | |
{ | |
public class ChangeTests | |
{ | |
public class GivenStruct | |
{ | |
[Fact] | |
public void GivenValue_ShouldShowChange() | |
{ | |
var sut = new Change<int> { Value = 1 }; | |
Assert.Equal(1, sut.Value); | |
Assert.Null(sut.Unset); | |
} | |
[Fact] | |
public void GivenValue_UsingImplicitConversion_ShouldShowChange() | |
{ | |
Change<int> sut = 1; | |
Assert.Equal(1, sut.Value); | |
Assert.Null(sut.Unset); | |
} | |
[Fact] | |
public void GivenOrEquals_AndNoChange_ShouldKeepOriginalValue() | |
{ | |
var a = 1; | |
Change<int> sut = null; | |
a |= sut; | |
Assert.Equal(1, a); | |
} | |
[Fact] | |
public void GivenOrEquals_AndChange_ShouldChangeValue() | |
{ | |
var a = 1; | |
var sut = new Change<int> { Value = 2 }; | |
a |= sut; | |
Assert.Equal(2, a); | |
} | |
} | |
public class GivenClass | |
{ | |
[Fact] | |
public void GivenValue_UsingImplicitConversion_ShouldShowChange() | |
{ | |
Change<int?> sut = 1; | |
Assert.Null(sut.Unset); | |
Assert.Equal(1, sut.Value); | |
} | |
[Fact] | |
public void GivenOrEquals_AndNoChange_ShouldKeepOriginalValue() | |
{ | |
int? a = 1; | |
Change<int?> sut = null; | |
a |= sut; | |
Assert.Equal(1, a); | |
} | |
[Fact] | |
public void GivenOrEquals_AndChangeWithNullValue_ShouldChangeValue() | |
{ | |
int? a = 1; | |
var sut = new Change<int?> { Unset = true }; | |
a |= sut; | |
Assert.Equal(null, a); | |
} | |
[Fact] | |
public void GivenOrEquals_AndChangeWithValue_AndNullLeft_ShouldChangeValue() | |
{ | |
int? a = null; | |
var sut = new Change<int?>{ Value = 1 }; | |
a |= sut; | |
Assert.Equal(1, a); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment