Skip to content

Commit

Permalink
Some simple refactoring using C#7 syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
olivercoad committed Dec 20, 2017
1 parent 3fcbaa8 commit 142dd49
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 89 deletions.
10 changes: 3 additions & 7 deletions Lifetime/DisposableLifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ namespace TwistedOak.Util {
public sealed class DisposableLifetime : IDisposable {
private readonly LifetimeSource _source = new LifetimeSource();
///<summary>The lifetime that transitions from mortal to dead when the managing DisposableLifetime is disposed.</summary>
public Lifetime Lifetime { get { return _source.Lifetime; } }
public Lifetime Lifetime => _source.Lifetime;
///<summary>Transitions the exposed lifetime from mortal to dead.</summary>
public void Dispose() {
_source.EndLifetime();
}
public void Dispose() => _source.EndLifetime();
///<summary>Returns a text representation of the disposable lifetime's current state.</summary>
public override string ToString() {
return _source.ToString();
}
public override string ToString() => _source.ToString();
}
}
34 changes: 15 additions & 19 deletions Lifetime/Lifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@ public struct Lifetime : IEquatable<Lifetime> {
/// </summary>
public static readonly Lifetime Dead = new Lifetime(DeadSoul.Instance);

//Must default to ImmortalSould.Instance in property rather than using auto-prop and setting default
//in contructor so that default(Lifetime).Soul is ImmortalSoul.Instance not null
private readonly ISoul _defSoul;
internal ISoul Soul { get { return _defSoul ?? ImmortalSoul.Instance; } }
internal ISoul Soul => _defSoul ?? ImmortalSoul.Instance;
internal Lifetime(ISoul soul) {
this._defSoul = soul;
_defSoul = soul;
}

/// <summary>Determines if this lifetime is still transiently mortal.</summary>
public bool IsMortal { get { return Soul.Phase == Phase.Mortal; } }
/// <summary>Determines if this lifetime is permanently immortal.</summary>
public bool IsImmortal { get { return Soul.Phase == Phase.Immortal; } }
/// <summary>Determines if this lifetime is permanently dead.</summary>
public bool IsDead { get { return Soul.Phase == Phase.Dead; } }
///<summary>Determines if this lifetime is still transiently mortal.</summary>
public bool IsMortal => Soul.Phase == Phase.Mortal;
///<summary>Determines if this lifetime is permanently immortal.</summary>
public bool IsImmortal => Soul.Phase == Phase.Immortal;
///<summary>Determines if this lifetime is permanently dead.</summary>
public bool IsDead => Soul.Phase == Phase.Dead;

/// <summary>
/// Registers an action to perform when this lifetime is dead.
Expand All @@ -49,7 +51,7 @@ internal Lifetime(ISoul soul) {
/// Defaults to an immortal lifetime.
/// </param>
public void WhenDead(Action action, Lifetime registrationLifetime = default(Lifetime)) {
if (action == null) throw new ArgumentNullException("action");
if (action == null) throw new ArgumentNullException(nameof(action));
var s = Soul;
s.DependentRegister(
() => { if (s.Phase == Phase.Dead) action(); },
Expand Down Expand Up @@ -94,19 +96,13 @@ public static implicit operator Lifetime(CancellationToken token) {
return source.Lifetime;
}

///<summary>Determines if the other lifetime has the same source.</summary>
/// <summary>Determines if the other lifetime has the same source.</summary>
/// <param name="other">The lifetime that this lifetime is being compared to.</param>
public bool Equals(Lifetime other) {
return Equals(Soul, other.Soul);
}
public bool Equals(Lifetime other) => Equals(Soul, other.Soul);
///<summary>Returns the hash code for this lifetime, based on its source.</summary>
public override int GetHashCode() {
return Soul.GetHashCode();
}
public override int GetHashCode() => Soul.GetHashCode();
///<summary>Determines if the other object is a lifetime with the same source.</summary>
public override bool Equals(object obj) {
return obj is Lifetime && Equals((Lifetime)obj);
}
public override bool Equals(object obj) => obj is Lifetime && Equals((Lifetime)obj);
///<summary>Returns a text representation of the lifetime's current state.</summary>
public override string ToString() {
if (Soul.Phase == Phase.Mortal) return "Alive";
Expand Down
4 changes: 2 additions & 2 deletions Lifetime/Lifetime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<PackageId>TwistedOak.Util.Lifetime</PackageId>
<Version>1.0.3</Version>
<Authors>Craig Gidney</Authors>
<Company>--company</Company>
<Product>--product</Product>
<Company>Twisted Oak Studios</Company>
<Product></Product>
<Description>Lifetime (improved CancellationToken)

A small library that implements an improved version of System.Threading.CancellationToken with support for removable registrations, to allow for garbage collection in the presence of long-lived tokens, plus some related utilities.
Expand Down
6 changes: 3 additions & 3 deletions Lifetime/LifetimeExchanger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ namespace TwistedOak.Util {
///<summary>Creates lifetimes when requested, setting them when the next lifetime is requested.</summary>
public sealed class LifetimeExchanger {
private LifetimeSource _active = new LifetimeSource();

///<summary>Returns the current lifetime, that will be killed or immortalized before the next lifetime is created by the exchanger.</summary>
public Lifetime ActiveLifetime { get { return _active.Lifetime; } }
public Lifetime ActiveLifetime => _active.Lifetime;

///<summary>Returns a newly created mortal lifetime after killing the previously created lifetime (if any).</summary>
public Lifetime StartNextAndEndPreviousLifetime() {
Expand All @@ -20,7 +20,7 @@ public Lifetime StartNextAndEndPreviousLifetime() {
public Lifetime StartNextAndImmortalizePreviousLifetime() {
var next = new LifetimeSource();
var prev = Interlocked.Exchange(ref _active, next);
if (prev != null) prev.ImmortalizeLifetime();
prev?.ImmortalizeLifetime();
return next.Lifetime;
}
}
Expand Down
10 changes: 4 additions & 6 deletions Lifetime/LifetimeSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace TwistedOak.Util {
[DebuggerDisplay("{ToString()}")]
public sealed class LifetimeSource {
private readonly MortalSoul _soul = new MortalSoul();
/// <summary>The lifetime exposed and managed by the lifetime source.</summary>
public Lifetime Lifetime { get; private set; }

///<summary>The lifetime exposed and managed by the lifetime source.</summary>
public Lifetime Lifetime { get; }

///<summary>Creates a new lifetime source managing a new initially mortal lifetime.</summary>
public LifetimeSource() {
Expand Down Expand Up @@ -45,8 +45,6 @@ public void ImmortalizeLifetime() {
}

///<summary>Returns a text representation of the lifetime source's current state.</summary>
public override string ToString() {
return Lifetime.ToString();
}
public override string ToString() => Lifetime.ToString();
}
}
14 changes: 5 additions & 9 deletions Lifetime/Soul/AnonymousSoul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@
using System.Diagnostics;

namespace TwistedOak.Util.Soul {
///<summary>A soul implemented by delegates passed to its constructor.</summary>
/// <summary>A soul implemented by delegates passed to its constructor.</summary>
[DebuggerStepThrough]
internal sealed class AnonymousSoul : ISoul {
private readonly Func<Phase> _phase;
private readonly Func<Action, RegistrationRemover> _register;
public AnonymousSoul(Func<Phase> phase, Func<Action, RegistrationRemover> register) {
if (phase == null) throw new ArgumentNullException("phase");
if (register == null) throw new ArgumentNullException("register");
this._phase = phase;
this._register = register;
_phase = phase ?? throw new ArgumentNullException(nameof(phase));
_register = register ?? throw new ArgumentNullException(nameof(register));
}

public Phase Phase { get { return this._phase(); } }
public RegistrationRemover Register(Action action) {
return this._register(action);
}
public Phase Phase => _phase();
public RegistrationRemover Register(Action action) => _register(action);
}
}
25 changes: 13 additions & 12 deletions Lifetime/Soul/CollapsingSoul.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
using System;

namespace TwistedOak.Util.Soul {
///<summary>Delegates to an underlying soul that is replaced with one of the permanent souls once it is no longer mortal.</summary>
/// <summary>
/// Delegates to an underlying soul that is replaced with one of the permanent
/// souls once it is no longer mortal.
/// </summary>
internal sealed class CollapsingSoul : ISoul {
private bool _collapsed;
private ISoul _subSoul;

public CollapsingSoul(ISoul subSoul) {
this._subSoul = subSoul;
_subSoul = subSoul;

// flatten multiple levels of wrapping
var r = subSoul as CollapsingSoul;
if (r != null) this._subSoul = r._subSoul;

if(subSoul is CollapsingSoul cS) _subSoul = cS._subSoul;

// ensure collapse occurs once the sub soul becomes fixed
Register(() => TryOptimize());
Register(() => tryOptimize());
}

private Phase TryOptimize() {
private Phase tryOptimize() {
var phase = _subSoul.Phase;
if (_collapsed) return phase; // already optimized
if (phase == Phase.Mortal) return phase; // can't optimize yet
Expand All @@ -27,9 +29,8 @@ private Phase TryOptimize() {
_subSoul = Phase.AsPermanentSoul();
return phase;
}
public Phase Phase { get { return TryOptimize(); } }
public RegistrationRemover Register(Action action) {
return _subSoul.Register(action);
}

public Phase Phase => tryOptimize();
public RegistrationRemover Register(Action action) => _subSoul.Register(action);
}
}
2 changes: 1 addition & 1 deletion Lifetime/Soul/DeadSoul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal sealed class DeadSoul : ISoul {
///<summary>The single instance of the permanently dead soul.</summary>
public static readonly ISoul Instance = new DeadSoul();
private DeadSoul() {}
public Phase Phase { get { return Phase.Dead; } }
public Phase Phase => Phase.Dead;
public RegistrationRemover Register(Action action) {
action();
return Soul.EmptyRemover;
Expand Down
28 changes: 17 additions & 11 deletions Lifetime/Soul/DoublyLinkedNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ internal sealed class DoublyLinkedNode<T> {
private DoublyLinkedNode<T> _next;
private DoublyLinkedNode<T> _prev;
private readonly T _item;

///<summary>Creates a node doubly linked to itself.</summary>
public static DoublyLinkedNode<T> CreateEmptyCycle() {
return new DoublyLinkedNode<T>();
}
public static DoublyLinkedNode<T> CreateEmptyCycle()
=> new DoublyLinkedNode<T>();

private DoublyLinkedNode() {
this._next = this._prev = this;
_next = _prev = this;
}

///<summary>Creates a new node containing the given item, inserted preceeding this node.</summary>
public DoublyLinkedNode<T> Prepend(T item) {
return new DoublyLinkedNode<T>(item, this);
}
/// <summary>
/// Creates a new node containing the given item,
/// inserted preceeding this node.
/// </summary>
public DoublyLinkedNode<T> Prepend(T item)
=> new DoublyLinkedNode<T>(item, this);

private DoublyLinkedNode(T item, DoublyLinkedNode<T> next) {
this._item = item;
this._next = next;
Expand All @@ -35,13 +38,16 @@ public void Unlink() {
this._next = this._prev = this;
}

///<summary>Enumerates all the nodes, except this one, that are in the same circular linked list.</summary>
/// <summary>
/// Enumerates all the nodes, except this one,
/// that are in the same circular linked list.
/// </summary>
public IEnumerable<T> EnumerateOthers() {
var h = this;
while (true) {
var n = h._next;
if (n == this) break;
if (ReferenceEquals(n, h))
if (ReferenceEquals(n, h))
throw new InvalidOperationException("List destructively modified while being enumerated.");
h = n;
yield return h._item;
Expand Down
2 changes: 1 addition & 1 deletion Lifetime/Soul/ImmortalSoul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal sealed class ImmortalSoul : ISoul {
///<summary>The single instance of the permanently dead soul.</summary>
public static readonly ISoul Instance = new ImmortalSoul();
private ImmortalSoul() { }
public Phase Phase { get { return Phase.Immortal; } }
public Phase Phase => Phase.Immortal;
public RegistrationRemover Register(Action action) {
action();
return Soul.EmptyRemover;
Expand Down
5 changes: 3 additions & 2 deletions Lifetime/Soul/MortalSoul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void TransitionPermanently(Phase newPhase) {
Phase = newPhase;

// callbacks
callbacks = _callbacks == null ? null : _callbacks.EnumerateOthers().ToArray();
callbacks = _callbacks?.EnumerateOthers().ToArray();
_callbacks = null;
}
if (callbacks != null)
Expand All @@ -54,7 +54,8 @@ public RegistrationRemover Register(Action action) {
}

// add callback for when finished
if (_callbacks == null) _callbacks = DoublyLinkedNode<Action>.CreateEmptyCycle();
if (_callbacks == null)
_callbacks = DoublyLinkedNode<Action>.CreateEmptyCycle();
weakNode = new WeakReference(_callbacks.Prepend(action));
}

Expand Down
22 changes: 11 additions & 11 deletions Lifetime/Soul/Soul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,20 @@ internal static class Soul {
/// Returns a soul permanently stuck in the given phase.
/// A permanently mortal soul is considered to be immortal.
/// </summary>
public static ISoul AsPermanentSoul(this Phase phase) {
return phase == Phase.Dead
? DeadSoul.Instance
public static ISoul AsPermanentSoul(this Phase phase)
=> phase == Phase.Dead
? DeadSoul.Instance
: ImmortalSoul.Instance;
}
/// <summary>
/// Returns a lifetime permanently stuck in the given phase.
/// A permanently mortal soul is considered to be immortal.
/// </summary>
public static Lifetime AsPermanentLifetime(this Phase phase) {
return new Lifetime(phase.AsPermanentSoul());
}
public static Lifetime AsPermanentLifetime(this Phase phase) => new Lifetime(phase.AsPermanentSoul());
///<summary>Returns a lifetime for the given soul, collapsing it to a simpler soul when possible.</summary>
public static Lifetime AsCollapsingLifetime(this ISoul soul) {
// avoid any wrapping if possible
var p = soul.Phase;
if (p != Phase.Mortal) return p.AsPermanentLifetime();
var phase = soul.Phase;
if (phase != Phase.Mortal) return phase.AsPermanentLifetime();

return new Lifetime(new CollapsingSoul(soul));
}
Expand Down Expand Up @@ -60,7 +57,10 @@ public static RegistrationRemover InterdependentRegister(this ISoul soul1, Func<
};
}

///<summary>Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first, ensuring everything is cleaned up properly.</summary>
/// <summary>
/// Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first,
/// ensuring everything is cleaned up properly.
/// </summary>
public static RegistrationRemover DependentRegister(this ISoul soul, Action action, ISoul necessarySoul) {
if (soul == null) throw new ArgumentNullException("soul");
if (action == null) throw new ArgumentNullException("action");
Expand Down Expand Up @@ -91,7 +91,7 @@ public static RegistrationRemover DependentRegister(this ISoul soul, Action acti

///<summary>Combines two souls by using a custom function to combine their phases.</summary>
public static ISoul Combine(this ISoul soul1, ISoul soul2, Func<Phase, Phase, Phase> phaseCombiner) {
Func<Phase> getPhase = () => phaseCombiner(soul1.Phase, soul2.Phase);
Phase getPhase() => phaseCombiner(soul1.Phase, soul2.Phase);
return new AnonymousSoul(
getPhase,
action => {
Expand Down
9 changes: 4 additions & 5 deletions LifetimeExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
using System.Threading;
using TwistedOak.Util;

public static class Program {
private static void WriteLine(this string text, params object[] arg) {
Console.WriteLine(text, arg);
}
public static class Program
{
private static void WriteLine(this string text, params object[] arg) => Console.WriteLine(text, arg);
private static void Break() {
Console.WriteLine("---");
Console.ReadLine();
Expand Down Expand Up @@ -120,7 +119,7 @@ private static void CallbackGarbageTest<T>(string name, Func<T> lostTokenMaker,
new Action(() => {
// register many callbacks with closures containing large amounts of memory be run when the lifetime/token ends/cancels
// (these callbacks will never run; the sources are collected)
for (var i = 0; i < 2000; i++) {
for(var i = 0; i < 2000; i++) {
var r = new byte[2000];
callbackRegistrar(
lostToken,
Expand Down

0 comments on commit 142dd49

Please sign in to comment.