public override bool Equals(object comparand)
{
if ((object)comparand == null)
{
return false;
}
if ((object)this == (object)comparand)
{
return true;
}
Uri obj = comparand as Uri;
//
// we allow comparisons of Uri and String objects only. If a string
// is passed, convert to Uri. This is inefficient, but allows us to
// canonicalize the comparand, making comparison possible
//
if ((object)obj == null)
{
string s = comparand as string;
if ((object)s == null)
return false;
if (!TryCreate(s, UriKind.RelativeOrAbsolute, out obj))
return false;
}
// Since v1.0 two Uris are equal if everything but fragment and UserInfo does match
// This check is for a case where we already fixed up the equal references
if ((object)_string == (object)obj._string)
{
return true;
}
if (IsAbsoluteUri != obj.IsAbsoluteUri)
return false;
if (IsNotAbsoluteUri)
return OriginalString.Equals(obj.OriginalString);
if (NotAny(Flags.AllUriInfoSet) || obj.NotAny(Flags.AllUriInfoSet))
{
// Try raw compare for m_Strings as the last chance to keep the working set small
if (!IsUncOrDosPath)
{
if (_string.Length == obj._string.Length)
{
unsafe
{
// Try case sensitive compare on m_Strings
fixed (char* selfPtr = _string)
{
fixed (char* otherPtr = obj._string)
{
// This will never go negative since m_String is checked to be a valid URI
int i = (_string.Length - 1);
for (; i >= 0; --i)
{
if (*(selfPtr + i) != *(otherPtr + i))
{
break;
}
}
if (i == -1)
{
return true;
}
}
}
}
}
}
else if (string.Compare(_string, obj._string, StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
}
// Note that equality test will bring the working set of both
// objects up to creation of m_Info.MoreInfo member
EnsureUriInfo();
obj.EnsureUriInfo();
if (!UserDrivenParsing && !obj.UserDrivenParsing && Syntax.IsSimple && obj.Syntax.IsSimple)
{
// Optimization of canonical DNS names by avoiding host string creation.
// Note there could be explicit ports specified that would invalidate path offsets
if (InFact(Flags.CanonicalDnsHost) && obj.InFact(Flags.CanonicalDnsHost))
{
ushort i1 = _info.Offset.Host;
ushort end1 = _info.Offset.Path;
ushort i2 = obj._info.Offset.Host;
ushort end2 = obj._info.Offset.Path;
string str = obj._string;
//Taking the shortest part
if (end1 - i1 > end2 - i2)
{
end1 = (ushort)(i1 + end2 - i2);
}
// compare and break on ':' if found
while (i1 < end1)
{
if (_string[i1] != str[i2])
{
return false;
}
if (str[i2] == ':')
{
// The other must have ':' too to have equal host
break;
}
++i1; ++i2;
}
// The longest host must have ':' or be of the same size
if (i1 < _info.Offset.Path && _string[i1] != ':')
{
return false;
}
if (i2 < end2 && str[i2] != ':')
{
return false;
}
//hosts are equal!
}
else
{
EnsureHostString(false);
obj.EnsureHostString(false);
if (!_info.Host.Equals(obj._info.Host))
{
return false;
}
}
if (Port != obj.Port)
{
return false;
}
}
// We want to cache RemoteUrl to improve perf for Uri as a key.
// We should consider reducing the overall working set by not caching some other properties mentioned in MoreInfo
UriInfo selfInfo = _info;
UriInfo otherInfo = obj._info;
if ((object)selfInfo.MoreInfo == null)
{
selfInfo.MoreInfo = new MoreInfo();
}
if ((object)otherInfo.MoreInfo == null)
{
otherInfo.MoreInfo = new MoreInfo();
}
// NB: To avoid a race condition when creating MoreInfo field
// "selfInfo" and "otherInfo" shall remain as local copies.
string selfUrl = selfInfo.MoreInfo.RemoteUrl;
if ((object)selfUrl == null)
{
selfUrl = GetParts(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped);
selfInfo.MoreInfo.RemoteUrl = selfUrl;
}
string otherUrl = otherInfo.MoreInfo.RemoteUrl;
if ((object)otherUrl == null)
{
otherUrl = obj.GetParts(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped);
otherInfo.MoreInfo.RemoteUrl = otherUrl;
}
if (!IsUncOrDosPath)
{
if (selfUrl.Length != otherUrl.Length)
{
return false;
}
unsafe
{
// Try case sensitive compare on m_Strings
fixed (char* seltPtr = selfUrl)
{
fixed (char* otherPtr = otherUrl)
{
char* endSelf = seltPtr + selfUrl.Length;
char* endOther = otherPtr + selfUrl.Length;
while (endSelf != seltPtr)
{
if (*--endSelf != *--endOther)
{
return false;
}
}
return true;
}
}
}
}
// if IsUncOrDosPath is true then we ignore case in the path comparison
// Get Unescaped form as most safe for the comparison
// Fragment AND UserInfo are ignored
//
return (string.Compare(selfInfo.MoreInfo.RemoteUrl,
otherInfo.MoreInfo.RemoteUrl,
IsUncOrDosPath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0);
}