From 4d264be7134075ab5f01e02fd0f130daa47b07d9 Mon Sep 17 00:00:00 2001 From: Jeff Walker Date: Wed, 30 Dec 2020 13:37:38 -0800 Subject: [PATCH] implement IEquatable, fix Equals, improve GetHashCode (issue #34) --- Semver.Test/SemVersionComparisonTests.cs | 5 +-- Semver/SemVersion.cs | 49 ++++++++++++++---------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/Semver.Test/SemVersionComparisonTests.cs b/Semver.Test/SemVersionComparisonTests.cs index 4c63050..71feb28 100644 --- a/Semver.Test/SemVersionComparisonTests.cs +++ b/Semver.Test/SemVersionComparisonTests.cs @@ -148,10 +148,9 @@ public void EqualsNullTest() public void EqualsNonSemVersionTest() { var v = new SemVersion(1); - // TODO should return false - var ex = Assert.Throws(() => v.Equals(new object())); + var r = v.Equals(new object()); - Assert.Equal("Unable to cast object of type 'System.Object' to type 'Semver.SemVersion'.", ex.Message); + Assert.False(r); } [Theory] diff --git a/Semver/SemVersion.cs b/Semver/SemVersion.cs index ce22a07..3247973 100644 --- a/Semver/SemVersion.cs +++ b/Semver/SemVersion.cs @@ -17,13 +17,13 @@ namespace Semver /// Conforms with v2.0.0 of http://semver.org /// #if NETSTANDARD - public sealed class SemVersion : IComparable, IComparable + public sealed class SemVersion : IComparable, IComparable, IEquatable #else [Serializable] public sealed class SemVersion : IComparable, IComparable, ISerializable #endif { - private const string InvalidSemVersionStylesMessage = "An invalid SemVersionStyles value was used."; + private const string InvalidSemVersionStylesMessage = "An invalid SemVersionStyles value was used"; /// /// This exception is used with the @@ -626,22 +626,31 @@ private static int CompareComponent(string a, string b, bool nonemptyIsLower = f /// /// if the specified is equal to this instance, otherwise . /// - /// The is not a . public override bool Equals(object obj) { - if (obj is null) + return Equals(obj as SemVersion); + } + + /// + /// Indicates whether the is equal to another . + /// + /// An object to compare with this object. + /// + /// if the current object is equal to the parameter, otherwise . + /// + public bool Equals(SemVersion other) + { + if (other is null) return false; - if (ReferenceEquals(this, obj)) + if (ReferenceEquals(this, other)) return true; - var other = (SemVersion)obj; - return Major == other.Major - && Minor == other.Minor - && Patch == other.Patch - && string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal) - && string.Equals(Metadata, other.Metadata, StringComparison.Ordinal); + && Minor == other.Minor + && Patch == other.Patch + && string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal) + && string.Equals(Metadata, other.Metadata, StringComparison.Ordinal); } /// @@ -652,16 +661,14 @@ public override bool Equals(object obj) /// public override int GetHashCode() { - unchecked - { - // TODO verify this. Some versions start result = 17. Some use 37 instead of 31 - int result = Major.GetHashCode(); - result = result * 31 + Minor.GetHashCode(); - result = result * 31 + Patch.GetHashCode(); - result = result * 31 + Prerelease.GetHashCode(); - result = result * 31 + Metadata.GetHashCode(); - return result; - } + // FNV based hash combine generated by VS + var hashCode = 276583904; + hashCode = hashCode * -1521134295 + Major.GetHashCode(); + hashCode = hashCode * -1521134295 + Minor.GetHashCode(); + hashCode = hashCode * -1521134295 + Patch.GetHashCode(); + hashCode = hashCode * -1521134295 + Prerelease.GetHashCode(); + hashCode = hashCode * -1521134295 + Metadata.GetHashCode(); + return hashCode; } #if !NETSTANDARD