From 142dd4904fa9b3fedefa2d08edd64d1bc4775d34 Mon Sep 17 00:00:00 2001 From: Oliver Coad Date: Wed, 20 Dec 2017 14:31:43 +1100 Subject: [PATCH] Some simple refactoring using C#7 syntax --- Lifetime/DisposableLifetime.cs | 10 +++------ Lifetime/Lifetime.cs | 34 ++++++++++++++----------------- Lifetime/Lifetime.csproj | 4 ++-- Lifetime/LifetimeExchanger.cs | 6 +++--- Lifetime/LifetimeSource.cs | 10 ++++----- Lifetime/Soul/AnonymousSoul.cs | 14 +++++-------- Lifetime/Soul/CollapsingSoul.cs | 25 ++++++++++++----------- Lifetime/Soul/DeadSoul.cs | 2 +- Lifetime/Soul/DoublyLinkedNode.cs | 28 +++++++++++++++---------- Lifetime/Soul/ImmortalSoul.cs | 2 +- Lifetime/Soul/MortalSoul.cs | 5 +++-- Lifetime/Soul/Soul.cs | 22 ++++++++++---------- LifetimeExample/Program.cs | 9 ++++---- 13 files changed, 82 insertions(+), 89 deletions(-) diff --git a/Lifetime/DisposableLifetime.cs b/Lifetime/DisposableLifetime.cs index 6ce1c67..8769f09 100644 --- a/Lifetime/DisposableLifetime.cs +++ b/Lifetime/DisposableLifetime.cs @@ -10,14 +10,10 @@ namespace TwistedOak.Util { public sealed class DisposableLifetime : IDisposable { private readonly LifetimeSource _source = new LifetimeSource(); ///The lifetime that transitions from mortal to dead when the managing DisposableLifetime is disposed. - public Lifetime Lifetime { get { return _source.Lifetime; } } + public Lifetime Lifetime => _source.Lifetime; ///Transitions the exposed lifetime from mortal to dead. - public void Dispose() { - _source.EndLifetime(); - } + public void Dispose() => _source.EndLifetime(); ///Returns a text representation of the disposable lifetime's current state. - public override string ToString() { - return _source.ToString(); - } + public override string ToString() => _source.ToString(); } } diff --git a/Lifetime/Lifetime.cs b/Lifetime/Lifetime.cs index 64bcceb..604be89 100644 --- a/Lifetime/Lifetime.cs +++ b/Lifetime/Lifetime.cs @@ -22,18 +22,20 @@ public struct Lifetime : IEquatable { /// 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; } - /// Determines if this lifetime is still transiently mortal. - public bool IsMortal { get { return Soul.Phase == Phase.Mortal; } } - /// Determines if this lifetime is permanently immortal. - public bool IsImmortal { get { return Soul.Phase == Phase.Immortal; } } - /// Determines if this lifetime is permanently dead. - public bool IsDead { get { return Soul.Phase == Phase.Dead; } } + ///Determines if this lifetime is still transiently mortal. + public bool IsMortal => Soul.Phase == Phase.Mortal; + ///Determines if this lifetime is permanently immortal. + public bool IsImmortal => Soul.Phase == Phase.Immortal; + ///Determines if this lifetime is permanently dead. + public bool IsDead => Soul.Phase == Phase.Dead; /// /// Registers an action to perform when this lifetime is dead. @@ -49,7 +51,7 @@ internal Lifetime(ISoul soul) { /// Defaults to an immortal lifetime. /// 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(); }, @@ -94,19 +96,13 @@ public static implicit operator Lifetime(CancellationToken token) { return source.Lifetime; } - ///Determines if the other lifetime has the same source. + /// Determines if the other lifetime has the same source. /// The lifetime that this lifetime is being compared to. - public bool Equals(Lifetime other) { - return Equals(Soul, other.Soul); - } + public bool Equals(Lifetime other) => Equals(Soul, other.Soul); ///Returns the hash code for this lifetime, based on its source. - public override int GetHashCode() { - return Soul.GetHashCode(); - } + public override int GetHashCode() => Soul.GetHashCode(); ///Determines if the other object is a lifetime with the same source. - 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); ///Returns a text representation of the lifetime's current state. public override string ToString() { if (Soul.Phase == Phase.Mortal) return "Alive"; diff --git a/Lifetime/Lifetime.csproj b/Lifetime/Lifetime.csproj index f34fc31..c112576 100644 --- a/Lifetime/Lifetime.csproj +++ b/Lifetime/Lifetime.csproj @@ -6,8 +6,8 @@ TwistedOak.Util.Lifetime 1.0.3 Craig Gidney - --company - --product + Twisted Oak Studios + 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. diff --git a/Lifetime/LifetimeExchanger.cs b/Lifetime/LifetimeExchanger.cs index 32a2234..cc0d507 100644 --- a/Lifetime/LifetimeExchanger.cs +++ b/Lifetime/LifetimeExchanger.cs @@ -4,9 +4,9 @@ namespace TwistedOak.Util { ///Creates lifetimes when requested, setting them when the next lifetime is requested. public sealed class LifetimeExchanger { private LifetimeSource _active = new LifetimeSource(); - + ///Returns the current lifetime, that will be killed or immortalized before the next lifetime is created by the exchanger. - public Lifetime ActiveLifetime { get { return _active.Lifetime; } } + public Lifetime ActiveLifetime => _active.Lifetime; ///Returns a newly created mortal lifetime after killing the previously created lifetime (if any). public Lifetime StartNextAndEndPreviousLifetime() { @@ -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; } } diff --git a/Lifetime/LifetimeSource.cs b/Lifetime/LifetimeSource.cs index 5226728..850dbfa 100644 --- a/Lifetime/LifetimeSource.cs +++ b/Lifetime/LifetimeSource.cs @@ -11,9 +11,9 @@ namespace TwistedOak.Util { [DebuggerDisplay("{ToString()}")] public sealed class LifetimeSource { private readonly MortalSoul _soul = new MortalSoul(); - - /// The lifetime exposed and managed by the lifetime source. - public Lifetime Lifetime { get; private set; } + + ///The lifetime exposed and managed by the lifetime source. + public Lifetime Lifetime { get; } ///Creates a new lifetime source managing a new initially mortal lifetime. public LifetimeSource() { @@ -45,8 +45,6 @@ public void ImmortalizeLifetime() { } ///Returns a text representation of the lifetime source's current state. - public override string ToString() { - return Lifetime.ToString(); - } + public override string ToString() => Lifetime.ToString(); } } diff --git a/Lifetime/Soul/AnonymousSoul.cs b/Lifetime/Soul/AnonymousSoul.cs index 593ea2c..6691f0a 100644 --- a/Lifetime/Soul/AnonymousSoul.cs +++ b/Lifetime/Soul/AnonymousSoul.cs @@ -2,21 +2,17 @@ using System.Diagnostics; namespace TwistedOak.Util.Soul { - ///A soul implemented by delegates passed to its constructor. + /// A soul implemented by delegates passed to its constructor. [DebuggerStepThrough] internal sealed class AnonymousSoul : ISoul { private readonly Func _phase; private readonly Func _register; public AnonymousSoul(Func phase, Func 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); } } diff --git a/Lifetime/Soul/CollapsingSoul.cs b/Lifetime/Soul/CollapsingSoul.cs index 4a4ce04..f2977c4 100644 --- a/Lifetime/Soul/CollapsingSoul.cs +++ b/Lifetime/Soul/CollapsingSoul.cs @@ -1,23 +1,25 @@ using System; namespace TwistedOak.Util.Soul { - ///Delegates to an underlying soul that is replaced with one of the permanent souls once it is no longer mortal. + /// + /// Delegates to an underlying soul that is replaced with one of the permanent + /// souls once it is no longer mortal. + /// 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 @@ -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); } } diff --git a/Lifetime/Soul/DeadSoul.cs b/Lifetime/Soul/DeadSoul.cs index 45fbb9a..9835831 100644 --- a/Lifetime/Soul/DeadSoul.cs +++ b/Lifetime/Soul/DeadSoul.cs @@ -6,7 +6,7 @@ internal sealed class DeadSoul : ISoul { ///The single instance of the permanently dead soul. 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; diff --git a/Lifetime/Soul/DoublyLinkedNode.cs b/Lifetime/Soul/DoublyLinkedNode.cs index 75acc0f..4bb97ee 100644 --- a/Lifetime/Soul/DoublyLinkedNode.cs +++ b/Lifetime/Soul/DoublyLinkedNode.cs @@ -7,19 +7,22 @@ internal sealed class DoublyLinkedNode { private DoublyLinkedNode _next; private DoublyLinkedNode _prev; private readonly T _item; - + ///Creates a node doubly linked to itself. - public static DoublyLinkedNode CreateEmptyCycle() { - return new DoublyLinkedNode(); - } + public static DoublyLinkedNode CreateEmptyCycle() + => new DoublyLinkedNode(); + private DoublyLinkedNode() { - this._next = this._prev = this; + _next = _prev = this; } - ///Creates a new node containing the given item, inserted preceeding this node. - public DoublyLinkedNode Prepend(T item) { - return new DoublyLinkedNode(item, this); - } + /// + /// Creates a new node containing the given item, + /// inserted preceeding this node. + /// + public DoublyLinkedNode Prepend(T item) + => new DoublyLinkedNode(item, this); + private DoublyLinkedNode(T item, DoublyLinkedNode next) { this._item = item; this._next = next; @@ -35,13 +38,16 @@ public void Unlink() { this._next = this._prev = this; } - ///Enumerates all the nodes, except this one, that are in the same circular linked list. + /// + /// Enumerates all the nodes, except this one, + /// that are in the same circular linked list. + /// public IEnumerable 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; diff --git a/Lifetime/Soul/ImmortalSoul.cs b/Lifetime/Soul/ImmortalSoul.cs index 0093e0d..b13afcc 100644 --- a/Lifetime/Soul/ImmortalSoul.cs +++ b/Lifetime/Soul/ImmortalSoul.cs @@ -6,7 +6,7 @@ internal sealed class ImmortalSoul : ISoul { ///The single instance of the permanently dead soul. 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; diff --git a/Lifetime/Soul/MortalSoul.cs b/Lifetime/Soul/MortalSoul.cs index 543247d..843927f 100644 --- a/Lifetime/Soul/MortalSoul.cs +++ b/Lifetime/Soul/MortalSoul.cs @@ -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) @@ -54,7 +54,8 @@ public RegistrationRemover Register(Action action) { } // add callback for when finished - if (_callbacks == null) _callbacks = DoublyLinkedNode.CreateEmptyCycle(); + if (_callbacks == null) + _callbacks = DoublyLinkedNode.CreateEmptyCycle(); weakNode = new WeakReference(_callbacks.Prepend(action)); } diff --git a/Lifetime/Soul/Soul.cs b/Lifetime/Soul/Soul.cs index f5a55d2..6019300 100644 --- a/Lifetime/Soul/Soul.cs +++ b/Lifetime/Soul/Soul.cs @@ -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. /// - 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; - } /// /// Returns a lifetime permanently stuck in the given phase. /// A permanently mortal soul is considered to be immortal. /// - public static Lifetime AsPermanentLifetime(this Phase phase) { - return new Lifetime(phase.AsPermanentSoul()); - } + public static Lifetime AsPermanentLifetime(this Phase phase) => new Lifetime(phase.AsPermanentSoul()); ///Returns a lifetime for the given soul, collapsing it to a simpler soul when possible. 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)); } @@ -60,7 +57,10 @@ public static RegistrationRemover InterdependentRegister(this ISoul soul1, Func< }; } - ///Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first, ensuring everything is cleaned up properly. + /// + /// Registers a callback to the dependent soul that only occurs if the necessary soul doesn't die first, + /// ensuring everything is cleaned up properly. + /// 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"); @@ -91,7 +91,7 @@ public static RegistrationRemover DependentRegister(this ISoul soul, Action acti ///Combines two souls by using a custom function to combine their phases. public static ISoul Combine(this ISoul soul1, ISoul soul2, Func phaseCombiner) { - Func getPhase = () => phaseCombiner(soul1.Phase, soul2.Phase); + Phase getPhase() => phaseCombiner(soul1.Phase, soul2.Phase); return new AnonymousSoul( getPhase, action => { diff --git a/LifetimeExample/Program.cs b/LifetimeExample/Program.cs index 4afa3b8..0c92588 100644 --- a/LifetimeExample/Program.cs +++ b/LifetimeExample/Program.cs @@ -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(); @@ -120,7 +119,7 @@ private static void CallbackGarbageTest(string name, Func 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,