RFC2141提到:


  
  词汇对等的例子
  
  以下URN比较突出显示了词汇对等
  定义:

   1- URN:foo:a123,456
   2- urn:foo:a123,456
   3- urn:FOO:a123,456
   4- urn:foo:A123,456
   5- urn:foo:a123%2C456
   6- URN:FOO:a123%2c456

  
  URN 1、2和3在词汇上都是等效的。
  


随后的RFC8141保留了该等效性:


  2.1。命名空间标识符(NID)
  
  NID不区分大小写(例如,“ ISBN”和“ isbn”是等效的)。


我可以在.NET框架中轻松找到的URN的最接近表示形式是URI class。但是,它似乎没有完全遵守RFC的等效定义:

    [TestMethod]
    public void TestEquivalentUrnsAreBroken()
    {
        Assert.AreEqual(
            new Uri("URN:foo:a123,456"),
            new Uri("urn:foo:a123,456"));

        Assert.AreEqual(
            new Uri("urn:foo:a123,456"),
            new Uri("urn:FOO:a123,456"));
    }


在上面的代码示例中,第一个断言按预期工作,而第二个断言失败。

有什么合理的方法可以使URI类遵守等效性定义?还有其他我应该使用的类吗?

请注意,我已经找到了URN class,但是文档中提到不应直接使用它。

最佳答案

从.NET 4.8开始,Uri类不支持对urn:方案使用的特定解析器。也许可以这样理解,因为即使NID的比较规则指定不区分大小写,每个RFC 8141用来比较两个NSS的规则也将取决于特定名称空间定义的规则。

对于快速而肮脏的方法,您可以尝试使用Uri.Compare()方法。如果两个URI都相等,它将返回零,否则返回非零。

var u1 = new Uri("URN:foo:a123,456");
var u2 = new Uri("urn:foo:a123,456");
var u3 = new Uri("urn:FOO:a123,456");
var u4 = new Uri("urn:nope:a123,456");

Console.WriteLine(Uri.Compare(u1, u2, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase)); // 0
Console.WriteLine(Uri.Compare(u1, u3, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase)); // 0
Console.WriteLine(Uri.Compare(u2, u3, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase)); // 0
Console.WriteLine(Uri.Compare(u3, u4, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase)); // -8


对于更具冒险精神的方法,您可以按照以下方式进行操作。这将需要仔细思考以正确实施。此代码并非按原样使用,而是作为起点。

using System;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main()
    {
        var u1 = new Urn("URN:foo:a123,456");
        var u2 = new Urn("urn:foo:a123,456");
        var u3 = new Urn("urn:foo:a123,456");
        var u4 = new Urn("urn:FOO:a123,456");
        var u5 = new Urn("urn:not-this-one:a123,456");
        Console.WriteLine(u1 == u2); // True
        Console.WriteLine(u3 == u4); // True
        Console.WriteLine(u4 == u5); // False
    }

    public class Urn : Uri
    {
        public const string UrnScheme = "urn";
        private const RegexOptions UrnRegexOptions = RegexOptions.Singleline | RegexOptions.CultureInvariant;
        private static Regex UrnRegex = new Regex("^urn:(?<NID>[a-z|A-Z][a-z|A-Z|-]{0,30}[a-z|A-Z]):(?<NSS>.*)$", UrnRegexOptions);

        public string NID { get; }
        public string NSS { get; }

        public Urn(string s) : base(s, UriKind.Absolute)
        {
            if (this.Scheme != UrnScheme) throw new FormatException($"URN scheme must be '{UrnScheme}'.");
            var match = UrnRegex.Match(this.AbsoluteUri);
            if (!match.Success) throw new FormatException("URN's NID is invalid.");
            NID = match.Groups["NID"].Value;
            NSS = match.Groups["NSS"].Value;
        }

        public override bool Equals(object other)
        {
            if (ReferenceEquals(other, this)) return true;
            return
                other is Urn u &&
                string.Equals(NID, u.NID, StringComparison.InvariantCultureIgnoreCase) &&
                string.Equals(NSS, u.NSS, StringComparison.Ordinal);
        }

        public override int GetHashCode() => base.GetHashCode();

        public static bool operator == (Urn u1, Urn u2)
        {
            if (ReferenceEquals(u1, u2)) return true;
            if (ReferenceEquals(u1, null) || ReferenceEquals(u2, null)) return false;
            return u1.Equals(u2);
        }

        public static bool operator != (Urn u1, Urn u2)
        {
            if (ReferenceEquals(u1, u2)) return false;
            if (ReferenceEquals(u1, null) || ReferenceEquals(u2, null)) return true;
            return !u1.Equals(u2);
        }
    }
}

关于c# - 如何在.NET中表示URN(统一资源名称),以便Equals可以按预期工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51285047/

10-09 04:12