From c9b9959642ad811f77ac0ef06b72d283d1b9b1cb Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 23 Dec 2016 11:04:57 +0100 Subject: [PATCH 1/4] Return string value of Some or blank in Option.ToString() This renders Option.ToString() consistent with Nullable.ToString() and easier to substitute for. --- Optional.Tests/MaybeTests.cs | 18 +++++++++--------- Optional/Option_Maybe.cs | 12 +----------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Optional.Tests/MaybeTests.cs b/Optional.Tests/MaybeTests.cs index 93f30c8..29d12e3 100644 --- a/Optional.Tests/MaybeTests.cs +++ b/Optional.Tests/MaybeTests.cs @@ -150,19 +150,19 @@ public void Maybe_Hashing() [TestMethod] public void Maybe_StringRepresentation() { - Assert.AreEqual(Option.None().ToString(), "None"); - Assert.AreEqual(Option.None().ToString(), "None"); - Assert.AreEqual(Option.None().ToString(), "None"); + Assert.AreEqual(Option.None().ToString(), ""); + Assert.AreEqual(Option.None().ToString(), ""); + Assert.AreEqual(Option.None().ToString(), ""); - Assert.AreEqual(Option.Some(null).ToString(), "Some(null)"); - Assert.AreEqual(Option.Some(null).ToString(), "Some(null)"); + Assert.AreEqual(Option.Some(null).ToString(), ""); + Assert.AreEqual(Option.Some(null).ToString(), ""); - Assert.AreEqual(Option.Some(1).ToString(), "Some(1)"); - Assert.AreEqual(Option.Some(1).ToString(), "Some(1)"); - Assert.AreEqual(Option.Some("1").ToString(), "Some(1)"); + Assert.AreEqual(Option.Some(1).ToString(), "1"); + Assert.AreEqual(Option.Some(1).ToString(), "1"); + Assert.AreEqual(Option.Some("1").ToString(), "1"); var now = DateTime.Now; - Assert.AreEqual(Option.Some(now).ToString(), "Some(" + now.ToString() + ")"); + Assert.AreEqual(Option.Some(now).ToString(), now.ToString()); } [TestMethod] diff --git a/Optional/Option_Maybe.cs b/Optional/Option_Maybe.cs index 8785e1f..71a1581 100644 --- a/Optional/Option_Maybe.cs +++ b/Optional/Option_Maybe.cs @@ -97,17 +97,7 @@ public override int GetHashCode() /// A string that represents the current optional. public override string ToString() { - if (hasValue) - { - if (value == null) - { - return "Some(null)"; - } - - return string.Format("Some({0})", value); - } - - return "None"; + return hasValue && value != null ? value.ToString() : string.Empty; } /// From f4668ce387440134134863463ac69b0eb15ebef7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 23 Dec 2016 11:27:05 +0100 Subject: [PATCH 2/4] DebuggerDisplay for Option --- Optional/Option_Maybe.cs | 20 ++++++++++++++++++++ Portable.Optional/project.json | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Optional/Option_Maybe.cs b/Optional/Option_Maybe.cs index 71a1581..f1d7417 100644 --- a/Optional/Option_Maybe.cs +++ b/Optional/Option_Maybe.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; @@ -12,6 +13,7 @@ namespace Optional #if !NETSTANDARD [Serializable] #endif + [DebuggerDisplay("{" + nameof(DebugString) + "}")] public struct Option : IEquatable> { private readonly bool hasValue; @@ -91,6 +93,24 @@ public override int GetHashCode() return 0; } + string DebugString + { + get + { + if (hasValue) + { + if (value == null) + { + return "Some(null)"; + } + + return string.Format("Some({0})", value); + } + + return "None"; + } + } + /// /// Returns a string that represents the current optional. /// diff --git a/Portable.Optional/project.json b/Portable.Optional/project.json index 68f1424..905c2ea 100644 --- a/Portable.Optional/project.json +++ b/Portable.Optional/project.json @@ -3,7 +3,8 @@ "dependencies": { "Microsoft.NETCore.Portable.Compatibility": "1.0.1", "Microsoft.NETCore.Platforms": "1.0.1", - "System.Collections": "4.0.11" + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11" }, "frameworks": { "netstandard1.0": {} From f753f1a30cf57c78a88e36182440b9b4c0d5763a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 23 Dec 2016 12:43:45 +0100 Subject: [PATCH 3/4] Better integration of Option with string formatting Closes #20 --- Optional.Tests/MaybeTests.cs | 34 +++++++++++++++++++++++++++++++++- Optional/Option_Maybe.cs | 11 +++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Optional.Tests/MaybeTests.cs b/Optional.Tests/MaybeTests.cs index 29d12e3..548606c 100644 --- a/Optional.Tests/MaybeTests.cs +++ b/Optional.Tests/MaybeTests.cs @@ -1,6 +1,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.IO; using System.Runtime.Serialization.Formatters.Binary; @@ -166,7 +167,38 @@ public void Maybe_StringRepresentation() } [TestMethod] - public void Maybe_GetValue() + public void Maybe_Format() + { + Assert.AreEqual("", ((IFormattable) Option.None()).ToString(null, null)); + Assert.AreEqual("", ((IFormattable) Option.None()).ToString(null, null)); + Assert.AreEqual("", ((IFormattable) Option.None()).ToString(null, null)); + + Assert.AreEqual("", ((IFormattable) Option.Some(null)).ToString(null, null)); + Assert.AreEqual("", ((IFormattable) Option.Some(null)).ToString(null, null)); + + // Test number formatting using a culture that uses a different + // separator for grouping than the current culture. + + var numberFormatProvider = + CultureInfo.GetCultures(CultureTypes.SpecificCultures) + .First(ci => ci.NumberFormat.NumberGroupSeparator != NumberFormatInfo.CurrentInfo.NumberGroupSeparator); + + Assert.AreEqual(int.MaxValue.ToString("N0", numberFormatProvider), ((IFormattable) Option.Some(int.MaxValue)).ToString("N0", numberFormatProvider)); + Assert.AreEqual(int.MaxValue.ToString("N0", numberFormatProvider), ((IFormattable) Option.Some(int.MaxValue)).ToString("N0", numberFormatProvider)); + + // Test date formatting using a culture that would yield a + // different result than the current culture. + + var now = DateTime.Now; + var dateFormatProvider = + CultureInfo.GetCultures(CultureTypes.SpecificCultures) + .First(ci => now.ToString("D", ci) != now.ToLongDateString()); + + Assert.AreEqual(now.ToString("D", dateFormatProvider), ((IFormattable) Option.Some(now)).ToString("D", dateFormatProvider)); + } + + [TestMethod] + public void Maybe_GetValue() { var noneStruct = Option.None(); var noneNullable = Option.None(); diff --git a/Optional/Option_Maybe.cs b/Optional/Option_Maybe.cs index f1d7417..7a65ce9 100644 --- a/Optional/Option_Maybe.cs +++ b/Optional/Option_Maybe.cs @@ -14,7 +14,7 @@ namespace Optional [Serializable] #endif [DebuggerDisplay("{" + nameof(DebugString) + "}")] - public struct Option : IEquatable> + public struct Option : IEquatable>, IFormattable { private readonly bool hasValue; private readonly T value; @@ -391,5 +391,12 @@ public Option Filter(Func predicate) /// /// The filtered optional. public Option NotNull() => hasValue && value == null ? Option.None() : this; - } + + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + return hasValue + ? string.Format(formatProvider, "{0:" + format + "}", value) + : string.Empty; + } +} } From c2f0318bcc1fec5904c7242e20202b8b1fe04d8b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 24 Dec 2016 14:31:12 +0100 Subject: [PATCH 4/4] Simpler & less redundant IFormattable implementation Instead of hopping over another String.Format (and causing an allocation of another format string), delegate to the underlying implementation of IFormattable on the value if it exists, otherwise to Option's own ToString(). --- Optional/Option_Maybe.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Optional/Option_Maybe.cs b/Optional/Option_Maybe.cs index 7a65ce9..d7d146e 100644 --- a/Optional/Option_Maybe.cs +++ b/Optional/Option_Maybe.cs @@ -392,11 +392,9 @@ public Option Filter(Func predicate) /// The filtered optional. public Option NotNull() => hasValue && value == null ? Option.None() : this; - string IFormattable.ToString(string format, IFormatProvider formatProvider) - { - return hasValue - ? string.Format(formatProvider, "{0:" + format + "}", value) - : string.Empty; - } -} + string IFormattable.ToString(string format, IFormatProvider formatProvider) => + !hasValue + ? string.Empty + : (value as IFormattable)?.ToString(format, formatProvider) ?? ToString(); + } }