Skip to content

Commit 94bb125

Browse files
authored
Implement IEquatable on Uri (#103511)
1 parent 4b7fda5 commit 94bb125

File tree

3 files changed

+56
-41
lines changed

3 files changed

+56
-41
lines changed

src/libraries/System.Private.Uri/src/System/Uri.cs

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace System
1717
{
1818
[Serializable]
1919
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
20-
public partial class Uri : ISpanFormattable, ISerializable
20+
public partial class Uri : ISpanFormattable, IEquatable<Uri>, ISerializable
2121
{
2222
public static readonly string UriSchemeFile = UriParser.FileUri.SchemeName;
2323
public static readonly string UriSchemeFtp = UriParser.FtpUri.SchemeName;
@@ -1674,20 +1674,6 @@ string IFormattable.ToString(string? format, IFormatProvider? formatProvider) =>
16741674
return !uri1.Equals(uri2);
16751675
}
16761676

1677-
//
1678-
// Equals
1679-
//
1680-
// Overrides default function (in Object class)
1681-
//
1682-
// Assumes:
1683-
// <comparand> is an object of class Uri or String
1684-
//
1685-
// Returns:
1686-
// true if objects have the same value, else false
1687-
//
1688-
// Throws:
1689-
// Nothing
1690-
//
16911677
public override bool Equals([NotNullWhen(true)] object? comparand)
16921678
{
16931679
if (comparand is null)
@@ -1700,12 +1686,12 @@ public override bool Equals([NotNullWhen(true)] object? comparand)
17001686
return true;
17011687
}
17021688

1703-
Uri? obj = comparand as Uri;
1689+
Uri? other = comparand as Uri;
17041690

17051691
// we allow comparisons of Uri and String objects only. If a string
17061692
// is passed, convert to Uri. This is inefficient, but allows us to
17071693
// canonicalize the comparand, making comparison possible
1708-
if (obj is null)
1694+
if (other is null)
17091695
{
17101696
if (DisablePathAndQueryCanonicalization)
17111697
return false;
@@ -1716,28 +1702,48 @@ public override bool Equals([NotNullWhen(true)] object? comparand)
17161702
if (ReferenceEquals(s, OriginalString))
17171703
return true;
17181704

1719-
if (!TryCreate(s, UriKind.RelativeOrAbsolute, out obj))
1705+
if (!TryCreate(s, UriKind.RelativeOrAbsolute, out other))
17201706
return false;
17211707
}
17221708

1723-
if (DisablePathAndQueryCanonicalization != obj.DisablePathAndQueryCanonicalization)
1709+
return Equals(other);
1710+
}
1711+
1712+
/// <summary>
1713+
/// Compares two <see cref="Uri"/> instances for equality.
1714+
/// </summary>
1715+
/// <param name="other">The <see cref="Uri"/> to compare to this instance.</param>
1716+
/// <returns><see langword="true"/> if the two instances represent the same URI; otherwise, <see langword="false"/>.</returns>
1717+
public bool Equals([NotNullWhen(true)] Uri? other)
1718+
{
1719+
if (other is null)
1720+
{
1721+
return false;
1722+
}
1723+
1724+
if (ReferenceEquals(this, other))
1725+
{
1726+
return true;
1727+
}
1728+
1729+
if (DisablePathAndQueryCanonicalization != other.DisablePathAndQueryCanonicalization)
17241730
return false;
17251731

1726-
if (ReferenceEquals(OriginalString, obj.OriginalString))
1732+
if (ReferenceEquals(OriginalString, other.OriginalString))
17271733
{
17281734
return true;
17291735
}
17301736

1731-
if (IsAbsoluteUri != obj.IsAbsoluteUri)
1737+
if (IsAbsoluteUri != other.IsAbsoluteUri)
17321738
return false;
17331739

17341740
if (IsNotAbsoluteUri)
1735-
return OriginalString.Equals(obj.OriginalString);
1741+
return OriginalString.Equals(other.OriginalString);
17361742

1737-
if (NotAny(Flags.AllUriInfoSet) || obj.NotAny(Flags.AllUriInfoSet))
1743+
if (NotAny(Flags.AllUriInfoSet) || other.NotAny(Flags.AllUriInfoSet))
17381744
{
17391745
// Try raw compare for _strings as the last chance to keep the working set small
1740-
if (string.Equals(_string, obj._string, IsUncOrDosPath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
1746+
if (string.Equals(_string, other._string, IsUncOrDosPath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
17411747
{
17421748
return true;
17431749
}
@@ -1746,20 +1752,20 @@ public override bool Equals([NotNullWhen(true)] object? comparand)
17461752
// Note that equality test will bring the working set of both
17471753
// objects up to creation of _info.MoreInfo member
17481754
EnsureUriInfo();
1749-
obj.EnsureUriInfo();
1755+
other.EnsureUriInfo();
17501756

1751-
if (!UserDrivenParsing && !obj.UserDrivenParsing && Syntax!.IsSimple && obj.Syntax.IsSimple)
1757+
if (!UserDrivenParsing && !other.UserDrivenParsing && Syntax!.IsSimple && other.Syntax.IsSimple)
17521758
{
17531759
// Optimization of canonical DNS names by avoiding host string creation.
17541760
// Note there could be explicit ports specified that would invalidate path offsets
1755-
if (InFact(Flags.CanonicalDnsHost) && obj.InFact(Flags.CanonicalDnsHost))
1761+
if (InFact(Flags.CanonicalDnsHost) && other.InFact(Flags.CanonicalDnsHost))
17561762
{
17571763
int i1 = _info.Offset.Host;
17581764
int end1 = _info.Offset.Path;
17591765

1760-
int i2 = obj._info.Offset.Host;
1761-
int end2 = obj._info.Offset.Path;
1762-
string str = obj._string;
1766+
int i2 = other._info.Offset.Host;
1767+
int end2 = other._info.Offset.Path;
1768+
string str = other._string;
17631769
//Taking the shortest part
17641770
if (end1 - i1 > end2 - i2)
17651771
{
@@ -1794,14 +1800,14 @@ public override bool Equals([NotNullWhen(true)] object? comparand)
17941800
else
17951801
{
17961802
EnsureHostString(false);
1797-
obj.EnsureHostString(false);
1798-
if (!_info.Host!.Equals(obj._info.Host))
1803+
other.EnsureHostString(false);
1804+
if (!_info.Host!.Equals(other._info.Host))
17991805
{
18001806
return false;
18011807
}
18021808
}
18031809

1804-
if (Port != obj.Port)
1810+
if (Port != other.Port)
18051811
{
18061812
return false;
18071813
}
@@ -1811,21 +1817,21 @@ public override bool Equals([NotNullWhen(true)] object? comparand)
18111817
// We should consider reducing the overall working set by not caching some other properties mentioned in MoreInfo
18121818

18131819
MoreInfo selfInfo = _info.MoreInfo;
1814-
MoreInfo otherInfo = obj._info.MoreInfo;
1820+
MoreInfo otherInfo = other._info.MoreInfo;
18151821

18161822
// Fragment AND UserInfo (for non-mailto URIs) are ignored
18171823
UriComponents components = UriComponents.HttpRequestUrl;
18181824

18191825
if (_syntax.InFact(UriSyntaxFlags.MailToLikeUri))
18201826
{
1821-
if (!obj._syntax.InFact(UriSyntaxFlags.MailToLikeUri))
1827+
if (!other._syntax.InFact(UriSyntaxFlags.MailToLikeUri))
18221828
return false;
18231829

18241830
components |= UriComponents.UserInfo;
18251831
}
18261832

18271833
string selfUrl = selfInfo.RemoteUrl ??= GetParts(components, UriFormat.SafeUnescaped);
1828-
string otherUrl = otherInfo.RemoteUrl ??= obj.GetParts(components, UriFormat.SafeUnescaped);
1834+
string otherUrl = otherInfo.RemoteUrl ??= other.GetParts(components, UriFormat.SafeUnescaped);
18291835

18301836
// if IsUncOrDosPath is true then we ignore case in the path comparison
18311837
return string.Equals(selfUrl, otherUrl, IsUncOrDosPath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);

src/libraries/System.Runtime/ref/System.Runtime.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16274,7 +16274,7 @@ public partial class NewsStyleUriParser : System.UriParser
1627416274
{
1627516275
public NewsStyleUriParser() { }
1627616276
}
16277-
public partial class Uri : System.IFormattable, System.ISpanFormattable, System.Runtime.Serialization.ISerializable
16277+
public partial class Uri : System.IFormattable, System.ISpanFormattable, System.IEquatable<Uri>, System.Runtime.Serialization.ISerializable
1627816278
{
1627916279
public static readonly string SchemeDelimiter;
1628016280
public static readonly string UriSchemeFile;
@@ -16335,6 +16335,7 @@ protected virtual void Canonicalize() { }
1633516335
protected virtual void CheckSecurity() { }
1633616336
public static int Compare(System.Uri? uri1, System.Uri? uri2, System.UriComponents partsToCompare, System.UriFormat compareFormat, System.StringComparison comparisonType) { throw null; }
1633716337
public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? comparand) { throw null; }
16338+
public bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] Uri? other) { throw null; }
1633816339
[System.ObsoleteAttribute("Uri.Escape has been deprecated and is not supported.")]
1633916340
protected virtual void Escape() { }
1634016341
public static string EscapeDataString(string stringToEscape) { throw null; }

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Uri.MethodsTests.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,20 +395,28 @@ public void EqualsTest(Uri uri1, object obj, bool expected)
395395
{
396396
Uri uri2 = obj as Uri;
397397

398-
if (uri1 != null)
398+
if (uri1 is not null)
399399
{
400400
Assert.Equal(expected, uri1.Equals(obj));
401401

402-
if (uri2 != null && expected)
402+
if (uri2 is not null)
403403
{
404-
Assert.Equal(uri1.GetHashCode(), uri2.GetHashCode());
404+
Assert.Equal(expected, uri1.Equals(uri2));
405+
Assert.Equal(expected, uri2.Equals(uri1));
406+
407+
if (expected)
408+
{
409+
Assert.Equal(uri1.GetHashCode(), uri2.GetHashCode());
410+
}
405411
}
406412
}
407413

408-
if (!(obj is string))
414+
if (obj is not string)
409415
{
410416
Assert.Equal(expected, uri1 == uri2);
417+
Assert.Equal(expected, uri2 == uri1);
411418
Assert.Equal(!expected, uri1 != uri2);
419+
Assert.Equal(!expected, uri2 != uri1);
412420
}
413421
}
414422

0 commit comments

Comments
 (0)