عنوان:

‫بهبودهای NET 9. و C# 13 برای جستجوی دیکشنری (Dictionary)


نویسنده: وحید نصیری
تاریخ: ۱۴۰۳/۱۲/۲۷ ۰۸:۳۹
آدرس: www.dntips.ir
معرفی GetAlternateLookup<TKey, TValue, TAlternate>() (دریافت جستجوی جایگزین):
  • این متد جدید به کلاس‌های مشابه دیکشنری (مانند Dictionary، HashSet) اضافه شده است.
  • این امکان را فراهم می‌کند تا دیکشنری‌ها از کلیدهای جستجوی جایگزین پشتیبانی کنند.
  • وجود مشکل نیاز به تبدیل ReadOnlySpan<char> به string برای جستجوی دیکشنری را حل می‌کند، که منجر به تخصیص حافظه می‌شود مثال:
var dict = new Dictionary<string, int>
{
    { "foo", 10 },
    { "bar", 20 },
    { "baz", 30 }
};

var lookup = dict.GetAlternateLookup<ReadOnlySpan<char>>();

var keys = "foo, bar, baz";

foreach (Range range in keys.AsSpan().Split(','))
{
    ReadOnlySpan<char> key = keys.AsSpan(range).Trim();

    int value = lookup[key];
    Console.WriteLine(value);
}

مزایا:
  • جستجوهای کارآمد با استفاده مستقیم از ReadOnlySpan<char> یا ReadOnlySpan<byte> (محدوده فقط خواندنی بایت) را فعال می‌کند.
  • تخصیص حافظه را کاهش می‌دهد و عملکرد را بهبود می‌بخشد.
  • پردازش سریع‌تر را در سناریوهایی مانند سریال‌سازها (Serializers) تسهیل می‌کند، که اغلب داده‌های UTF-8 را مدیریت می‌کنند.

IAlternateEqualityComparer<in TAlternate, T> (مقایسه‌گر برابری جایگزین):
  • رابط جدید مورد نیاز برای جستجوهای جایگزین است.
  • روش‌هایی را برای مقایسه و هش کردن کلیدهای جایگزین تعریف می‌کند.
  • ویژگی allows ref struct در C# 13 استفاده از Span<T> را به عنوان آرگومان‌های نوع‌های عمومی فعال می‌کند.
public interface IAlternateEqualityComparer<in TAlternate, T>
    where TAlternate : allows ref struct
    where T : allows ref struct
{
    bool Equals(TAlternate alternate, T other);
    int GetHashCode(TAlternate alternate);
    T Create(TAlternate alternate);
}

مدیریت UTF-8:
  • مقاله، نمونه ای از Utf8StringEqualityComparer (مقایسه‌گر برابری رشته UTF-8) را برای پشتیبانی از جستجوهای ReadOnlySpan<byte> ارائه می‌دهد.
  • از ReadOnlySpan<T>.SequenceEqual (برابری دنباله) برای مقایسه سریع آرایه بایت استفاده می‌کند.
  • XxHash3 را برای تولید کد هش کارآمد توصیه می‌کند.
public sealed class Utf8StringEqualityComparer : IEqualityComparer<byte[]>, IAlternateEqualityComparer<ReadOnlySpan<byte>, byte[]>
{
    public static IEqualityComparer<byte[]> Default { get; } = new Utf8StringEqualityComparer();

    public bool Equals(byte[]? x, byte[]? y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;

        return x.AsSpan().SequenceEqual(y);
    }

    public int GetHashCode([DisallowNull] byte[] obj)
    {
        return GetHashCode(obj.AsSpan());
    }

    public byte[] Create(ReadOnlySpan<byte> alternate)
    {
        return alternate.ToArray();
    }

    public bool Equals(ReadOnlySpan<byte> alternate, byte[] other)
    {
        return other.AsSpan().SequenceEqual(alternate);
    }

    public int GetHashCode(ReadOnlySpan<byte> alternate)
    {
        return unchecked((int)XxHash3.HashToUInt64(alternate));
    }
}

موارد استفاده عملی:
  • مثال استفاده از GetAlternateLookup با Utf8JsonReader (خواننده JSON UTF-8) را برای جستجوهای ویژگی JSON کارآمد نشان می‌دهد.
  • عملکرد را در سریال‌سازهایی مانند MessagePack برای #C بهبود می‌بخشد.
var dict = new Dictionary<byte[], bool>(Utf8StringEqualityComparer.Default)
{
    { "foo"u8.ToArray(), true },
    { "bar"u8.ToArray(), false },
    { "baz"u8.ToArray(), false }
};

var lookup = dict.GetAlternateLookup<ReadOnlySpan<byte>>();

ReadOnlySpan<byte> json = """    
{
    "foo": 0,
    "bar": 0,
    "baz": 0
}
"""u8;

var reader = new Utf8JsonReader(json);

while (reader.Read())
{
    if (reader.TokenType == JsonTokenType.PropertyName)
    {
        ReadOnlySpan<byte> key = reader.ValueSpan;
        var flag = lookup[key];

        Console.WriteLine(flag);
    }
}

ملاحظات عملکرد:
  • به دلیل سربار رمزگذاری، از استفاده از هر دو string و ReadOnlySpan<byte> به عنوان کلیدهای جایگزین در یک دیکشنری خودداری کنید.
  • اگر هر دو نوع جستجو مورد نیاز است، استفاده از دو دیکشنری جداگانه ترجیح داده می‌شود.


مشاهده مطلب اصلی