.NET normal ifade kaynak oluşturucuları

Normal ifade veya regex, bir geliştiricinin aranmakta olan bir deseni ifade edebilmesini sağlayan bir dizedir. Bu dize, metni aramanın ve sonuçları arama dizesinden bir alt küme olarak ayıklamanın yaygın bir yoludur. .NET'te ad alanı örnekleri System.Text.RegularExpressions ve statik yöntemleri tanımlamak Regex ve kullanıcı tanımlı desenlerle eşleştirmek için kullanılır. Bu makalede, performansı iyileştirmek için örnek oluşturmak Regex için kaynak oluşturmayı kullanmayı öğreneceksiniz.

Not

Mümkün olduğunda, seçeneğini kullanarak RegexOptions.Compiled normal ifadeleri derlemek yerine kaynak tarafından oluşturulan normal ifadeleri kullanın. Kaynak oluşturma, uygulamanızın daha hızlı başlatılmasına, daha hızlı çalışmasına ve daha kırpılabilir hale getirmenize yardımcı olabilir. Kaynak oluşturmanın ne zaman mümkün olduğunu öğrenmek için bkz . Ne zaman kullanılır?

Derlenmiş normal ifadeler

yazdığınızda new Regex("somepattern")birkaç şey olur. Belirtilen desen, hem desenin geçerliliğini sağlamak hem de ayrıştırılmış regex'i temsil eden bir iç ağaca dönüştürmek için ayrıştırılır. Daha sonra ağaç çeşitli şekillerde iyileştirildiğinden desen daha verimli bir şekilde yürütülebilecek işlevsel olarak eşdeğer bir varyasyona dönüştürülür. Ağaç, regex yorumlayıcı altyapısına nasıl eşleştirileceğine ilişkin yönergeler sağlayan bir dizi işlem kodu ve işlenen olarak yorumlanabilen bir forma yazılır. Bir eşleşme gerçekleştirildiğinde, yorumlayıcı bu yönergelerin üzerinde ilerleyerek bunları giriş metnine göre işler. Yeni Regex bir örneğin örneğini oluştururken veya üzerinde Regexstatik yöntemlerden birini çağırırken yorumlayıcı, kullanılan varsayılan altyapıdır.

belirttiğinizde RegexOptions.Compiled, aynı inşaat zamanı çalışmalarının tümü gerçekleştirilir. Sonuçta elde edilen yönergeler yansıma yayma tabanlı derleyici tarafından birkaç DynamicMethod nesneye yazılan IL yönergelerine dönüştürülür. Bir eşleşme gerçekleştirildiğinde, bu DynamicMethod yöntemler çağrılır. Bu IL temelde tam olarak yorumlayıcının yapacağı şeyi yapar, ancak işlenen tam desen için özelleştirilmiştir. Örneğin, desen içeriyorsa [ac]yorumlayıcı "geçerli konumdaki giriş karakterini bu küme açıklamasında belirtilen kümeyle eşleştir" ifadesini içeren bir işlem kodu görür. Derlenen IL, etkili bir şekilde "veya 'c'" ile geçerli konumdaki 'a' giriş karakterini eşleştiren bir kod içerir. Bu özel büyük/küçük harf kullanımı ve desen bilgisi temelinde iyileştirmeler yapabilme özelliği, yorumlayıcıdan çok daha hızlı eşleşen aktarım hızı belirtmenin RegexOptions.Compiled temel nedenlerinden bazılarıdır.

'nin çeşitli dezavantajları RegexOptions.Compiledvardır. En etkili olan, oluşturmanın maliyetli olmasıdır. Yalnızca yorumlayıcı için ödenen maliyetlerin tümüyle aynı değildir, aynı zamanda sonuçta elde edilen RegexNode ağacı ve oluşturulan opcode'ları/işlenenleri IL'de derlemesi gerekir ve bu da önemsiz olmayan harcamalar ekler. Oluşturulan IL'nin ilk kullanımda JIT ile derlenmesi gerekir ve bu da başlangıçta daha fazla masrafa neden olur. RegexOptions.Compiled , ilk kullanımdaki ek yüklerle sonraki her kullanımdaki ek yük arasındaki temel dengeyi temsil eder. Kullanımı System.Reflection.Emit ayrıca belirli ortamlarda kullanımını RegexOptions.Compiled engeller; bazı işletim sistemleri dinamik olarak oluşturulan kodun yürütülmesine izin vermez ve bu tür sistemlerde Compiled kullanılamaz duruma gelir.

Kaynak oluşturma

.NET 7 yeni RegexGenerator bir kaynak oluşturucu tanıttı. Kaynak oluşturucu, derleyiciye takılan ve derleme birimini ek kaynak koduyla genişleten bir bileşendir. .NET SDK 'sı (sürüm 7 ve üzeri), döndüren Regexkısmi bir yöntemde özniteliğini tanıyan GeneratedRegexAttribute bir kaynak oluşturucu içerir. Kaynak oluşturucu, için tüm mantığı içeren bu yöntemin Regexbir uygulamasını sağlar. Örneğin, daha önce aşağıdaki gibi bir kod yazmış olabilirsiniz:

private static readonly Regex s_abcOrDefGeneratedRegex =
    new(pattern: "abc|def",
        options: RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static void EvaluateText(string text)
{
    if (s_abcOrDefGeneratedRegex.IsMatch(text))
    {
        // Take action with matching text
    }
}

Kaynak oluşturucuyu kullanmak için önceki kodu aşağıdaki gibi yeniden yazarsınız:

[GeneratedRegex("abc|def", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex AbcOrDefGeneratedRegex();

private static void EvaluateText(string text)
{
    if (AbcOrDefGeneratedRegex().IsMatch(text))
    {
        // Take action with matching text
    }
}

İpucu

Bayrağı RegexOptions.Compiled kaynak oluşturucu tarafından yoksayılır, bu nedenle kaynak tarafından oluşturulan sürümde gerekli değildir.

Oluşturulan uygulaması AbcOrDefGeneratedRegex() benzer şekilde tek Regex bir örneği önbelleğe alır, bu nedenle kodu kullanmak için ek önbelleğe alma gerekmez.

Aşağıdaki görüntü, kaynak oluşturucunun yaydığı alt sınıfa Regex kaynak internal tarafından oluşturulan önbelleğe alınmış örneğin ekran görüntüsüdür:

Önbelleğe alınmış regex statik alanı

Ama görüldüğü gibi, bu sadece .new Regex(...) Bunun yerine, kaynak oluşturucu C# kodu olarak, IL'de yayılana RegexOptions.Compiled benzer bir mantıkla özel Regextüretilmiş bir uygulama yayar. 'nin RegexOptions.Compiled tüm aktarım hızı performans avantajlarını (daha doğrusu) ve başlangıç avantajlarını Regex.CompileToAssembly, ancak karmaşıklığı CompileToAssemblyolmadan elde edersiniz. Yayılan kaynak projenizin bir parçasıdır ve bu da kolayca görüntülenebilir ve hata ayıklanabilir olduğu anlamına gelir.

Kaynak tarafından oluşturulan Regex koduyla hata ayıklama

İpucu

Visual Studio'da kısmi yöntem bildiriminize sağ tıklayın ve Tanıma Git'i seçin. Alternatif olarak, Çözüm Gezgini proje düğümünü seçin ve ardından bu regex oluşturucudan oluşturulan C# kodunu görmek için Dependencies>Analyzers>System.Text.RegularExpressions.Generator>System.Text.RegularExpressions.Generator.RegexGenerator>RegexGenerator.g.cs genişletin.

Bunun içinde kesme noktaları ayarlayabilir, adım adım ilerleyebilir ve bunu bir öğrenme aracı olarak kullanarak regex altyapısının girişinizle deseninizi tam olarak nasıl işlediğinden emin olabilirsiniz. Oluşturucu, ifadeyi bir bakışta anlaşılır hale getirmek ve nerede kullanıldığına yardımcı olmak için üçlü eğik çizgi (XML) açıklamaları bile oluşturur.

Regex'i açıklayan oluşturulan XML açıklamaları

Kaynak tarafından oluşturulan dosyaların içinde

.NET 7 ile hem kaynak oluşturucu hem RegexCompiler de neredeyse tamamen yeniden yazıldı ve temel olarak oluşturulan kodun yapısı değiştirildi. Bu yaklaşım, tüm yapıları işleyecek şekilde genişletilmiştir (tek bir uyarıyla) ve her ikisi RegexCompiler de ve kaynak oluşturucu yine de yeni yaklaşımı izleyerek çoğunlukla 1:1'i birbiriyle eşlemektedir. İfadedeki birincil işlevlerden biri için kaynak oluşturucu çıkışını abc|def göz önünde bulundurun:

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // Match with 2 alternative expressions, atomically.
    {
        if (slice.IsEmpty)
        {
            return false; // The input didn't match.
        }

        switch (slice[0])
        {
            case 'A' or 'a':
                if ((uint)slice.Length < 3 ||
                    !slice.Slice(1).StartsWith("bc", StringComparison.OrdinalIgnoreCase)) // Match the string "bc" (ordinal case-insensitive)
                {
                    return false; // The input didn't match.
                }

                pos += 3;
                slice = inputSpan.Slice(pos);
                break;

            case 'D' or 'd':
                if ((uint)slice.Length < 3 ||
                    !slice.Slice(1).StartsWith("ef", StringComparison.OrdinalIgnoreCase)) // Match the string "ef" (ordinal case-insensitive)
                {
                    return false; // The input didn't match.
                }

                pos += 3;
                slice = inputSpan.Slice(pos);
                break;

            default:
                return false; // The input didn't match.
        }
    }

    // The input matched.
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;
}
private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    int capture_starting_pos = 0;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // 1st capture group.
    //{
        capture_starting_pos = pos;

        // Match with 2 alternative expressions.
        //{
            if (slice.IsEmpty)
            {
                UncaptureUntil(0);
                return false; // The input didn't match.
            }

            switch (slice[0])
            {
                case 'a':
                    pos++;
                    slice = inputSpan.Slice(pos);
                    break;

                case 'b':
                    // Match 'c'.
                    if ((uint)slice.Length < 2 || slice[1] != 'c')
                    {
                        UncaptureUntil(0);
                        return false; // The input didn't match.
                    }

                    pos += 2;
                    slice = inputSpan.Slice(pos);
                    break;

                default:
                    UncaptureUntil(0);
                    return false; // The input didn't match.
            }
        //}

        base.Capture(1, capture_starting_pos, pos);
    //}

    // Match 'd'.
    if (slice.IsEmpty || slice[0] != 'd')
    {
        UncaptureUntil(0);
        return false; // The input didn't match.
    }

    // The input matched.
    pos++;
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;

    // <summary>Undo captures until it reaches the specified capture position.</summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    void UncaptureUntil(int capturePosition)
    {
        while (base.Crawlpos() > capturePosition)
        {
            base.Uncapture();
        }
    }
}

Kaynak tarafından oluşturulan kodun amacı, her adımda neler yapıldığını açıklayan açıklamalar ve genel olarak oluşturucunun kodu bir insan yazmış gibi yayması gerektiği yol gösteren kodla birlikte, takip edilmesi kolay bir yapı ile anlaşılabilir olmaktır. Geri izleme söz konusu olsa bile, geri izlemenin yapısı, bir sonraki atlamayı belirtmek için bir yığına güvenmek yerine kodun yapısının bir parçası haline gelir. Örneğin, ifade şu olduğunda oluşturulan eşleşen işlevin kodu aşağıda verilmiştir [ab]*[bc]:

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    int charloop_starting_pos = 0, charloop_ending_pos = 0;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // Match a character in the set [ABab] greedily any number of times.
    //{
        charloop_starting_pos = pos;

        int iteration = slice.IndexOfAnyExcept(Utilities.s_ascii_600000006000000);
        if (iteration < 0)
        {
            iteration = slice.Length;
        }

        slice = slice.Slice(iteration);
        pos += iteration;

        charloop_ending_pos = pos;
        goto CharLoopEnd;

        CharLoopBacktrack:

        if (Utilities.s_hasTimeout)
        {
            base.CheckTimeout();
        }

        if (charloop_starting_pos >= charloop_ending_pos ||
            (charloop_ending_pos = inputSpan.Slice(charloop_starting_pos, charloop_ending_pos - charloop_starting_pos).LastIndexOfAny(Utilities.s_ascii_C0000000C000000)) < 0)
        {
            return false; // The input didn't match.
        }
        charloop_ending_pos += charloop_starting_pos;
        pos = charloop_ending_pos;
        slice = inputSpan.Slice(pos);

        CharLoopEnd:
    //}

    // Advance the next matching position.
    if (base.runtextpos < pos)
    {
        base.runtextpos = pos;
    }

    // Match a character in the set [BCbc].
    if (slice.IsEmpty || ((uint)((slice[0] | 0x20) - 'b') > (uint)('c' - 'b')))
    {
        goto CharLoopBacktrack;
    }

    // The input matched.
    pos++;
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;
}
private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    int charloop_starting_pos = 0, charloop_ending_pos = 0;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // Match a character in the set [ABab] greedily any number of times.
    //{
        charloop_starting_pos = pos;

        int iteration = slice.IndexOfAnyExcept("ABab");
        if (iteration < 0)
        {
            iteration = slice.Length;
        }

        slice = slice.Slice(iteration);
        pos += iteration;

        charloop_ending_pos = pos;
        goto CharLoopEnd;

        CharLoopBacktrack:

        if (Utilities.s_hasTimeout)
        {
            base.CheckTimeout();
        }

        if (charloop_starting_pos >= charloop_ending_pos ||
            (charloop_ending_pos = inputSpan.Slice(charloop_starting_pos, charloop_ending_pos - charloop_starting_pos).LastIndexOfAny("BCbc")) < 0)
        {
            return false; // The input didn't match.
        }
        charloop_ending_pos += charloop_starting_pos;
        pos = charloop_ending_pos;
        slice = inputSpan.Slice(pos);

        CharLoopEnd:
    //}

    // Advance the next matching position.
    if (base.runtextpos < pos)
    {
        base.runtextpos = pos;
    }

    // Match a character in the set [BCbc].
    if (slice.IsEmpty || ((uint)((slice[0] | 0x20) - 'b') > (uint)('c' - 'b')))
    {
        goto CharLoopBacktrack;
    }

    // The input matched.
    pos++;
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;
}

Geri izlemenin yapısını kodda, geri izlemenin nereye geri döndürüleceği için etiketin yayıldığı ve goto regex'in sonraki bir bölümü başarısız olduğunda bu konuma atlamak için kullanılan bir etiketle CharLoopBacktrack görebilirsiniz.

Uygulayan RegexCompiler koda ve kaynak oluşturucuya bakarsanız, bunlar son derece benzer görünür: benzer adlandırılmış yöntemler, benzer çağrı yapısı ve hatta uygulama boyunca benzer açıklamalar. Çoğunlukla, biri IL'de, biri C# dilinde olsa da aynı kodla sonuçlanır. Elbette, C# derleyicisi daha sonra C# öğesini IL'ye çevirmekle sorumludur, bu nedenle her iki durumda da sonuçta elde edilen IL büyük olasılıkla aynı olmayacaktır. Kaynak oluşturucu, C# derleyicisinin çeşitli C# yapılarını daha da iyileştireceği gerçeğinden yararlanarak çeşitli durumlarda buna dayanır. Bu nedenle kaynak oluşturucunun değerinden daha iyileştirilmiş eşleşen kod RegexCompilerüretmesi için birkaç özel özellik vardır. Örneğin, önceki örneklerden birinde kaynak oluşturucunun için bir dal ve için başka 'b'bir dal 'a' içeren bir switch deyimi yaydığı görebilirsiniz. C# derleyicisi switch deyimlerini iyileştirme konusunda çok iyi olduğundan, bunu verimli bir şekilde yapmak için kullanabileceği birden çok strateji olduğundan, kaynak oluşturucunun bunu yapmayan RegexCompiler özel bir iyileştirmesi vardır. Değişimler için , kaynak oluşturucu tüm dallara bakar ve her dalın farklı bir başlangıç karakteriyle başladığını kanıtlayabilirse, bu ilk karakterin üzerine bir switch deyimi yayar ve bu değişiklik için herhangi bir geri izleme kodunun çıkışını yapmaktan kaçınır.

Bunun biraz daha karmaşık bir örneği aşağıda verilmişti. Değişiklikler, bunları geri izleme altyapıları tarafından daha kolay iyileştirilecek ve daha basit kaynak tarafından oluşturulan koda yol açacak şekilde yeniden düzenlemenin mümkün olup olmadığını belirlemek için daha yoğun bir şekilde analiz edilir. Bu tür iyileştirmelerden biri dallardan ortak ön eklerin ayıklanması destekler ve değişiklik, sıralamanın önemli olmadığı şekilde atomikse, daha fazla ayıklamaya olanak sağlamak için dalları yeniden sıralama. Bunun, aşağıdaki hafta içi düzeni Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sundayiçin etkisini görebilirsiniz. Bu, şuna benzer bir eşleşen işlev üretir:

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    char ch;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // Match with 6 alternative expressions, atomically.
    {
        int alternation_starting_pos = pos;

        // Branch 0
        {
            if ((uint)slice.Length < 6 ||
                !slice.StartsWith("monday", StringComparison.OrdinalIgnoreCase)) // Match the string "monday" (ordinal case-insensitive)
            {
                goto AlternationBranch;
            }

            pos += 6;
            slice = inputSpan.Slice(pos);
            goto AlternationMatch;

            AlternationBranch:
            pos = alternation_starting_pos;
            slice = inputSpan.Slice(pos);
        }

        // Branch 1
        {
            if ((uint)slice.Length < 7 ||
                !slice.StartsWith("tuesday", StringComparison.OrdinalIgnoreCase)) // Match the string "tuesday" (ordinal case-insensitive)
            {
                goto AlternationBranch1;
            }

            pos += 7;
            slice = inputSpan.Slice(pos);
            goto AlternationMatch;

            AlternationBranch1:
            pos = alternation_starting_pos;
            slice = inputSpan.Slice(pos);
        }

        // Branch 2
        {
            if ((uint)slice.Length < 9 ||
                !slice.StartsWith("wednesday", StringComparison.OrdinalIgnoreCase)) // Match the string "wednesday" (ordinal case-insensitive)
            {
                goto AlternationBranch2;
            }

            pos += 9;
            slice = inputSpan.Slice(pos);
            goto AlternationMatch;

            AlternationBranch2:
            pos = alternation_starting_pos;
            slice = inputSpan.Slice(pos);
        }

        // Branch 3
        {
            if ((uint)slice.Length < 8 ||
                !slice.StartsWith("thursday", StringComparison.OrdinalIgnoreCase)) // Match the string "thursday" (ordinal case-insensitive)
            {
                goto AlternationBranch3;
            }

            pos += 8;
            slice = inputSpan.Slice(pos);
            goto AlternationMatch;

            AlternationBranch3:
            pos = alternation_starting_pos;
            slice = inputSpan.Slice(pos);
        }

        // Branch 4
        {
            if ((uint)slice.Length < 6 ||
                !slice.StartsWith("fr", StringComparison.OrdinalIgnoreCase) || // Match the string "fr" (ordinal case-insensitive)
                ((((ch = slice[2]) | 0x20) != 'i') & (ch != 'İ')) || // Match a character in the set [Ii\u0130].
                !slice.Slice(3).StartsWith("day", StringComparison.OrdinalIgnoreCase)) // Match the string "day" (ordinal case-insensitive)
            {
                goto AlternationBranch4;
            }

            pos += 6;
            slice = inputSpan.Slice(pos);
            goto AlternationMatch;

            AlternationBranch4:
            pos = alternation_starting_pos;
            slice = inputSpan.Slice(pos);
        }

        // Branch 5
        {
            // Match a character in the set [Ss].
            if (slice.IsEmpty || ((slice[0] | 0x20) != 's'))
            {
                return false; // The input didn't match.
            }

            // Match with 2 alternative expressions, atomically.
            {
                if ((uint)slice.Length < 2)
                {
                    return false; // The input didn't match.
                }

                switch (slice[1])
                {
                    case 'A' or 'a':
                        if ((uint)slice.Length < 8 ||
                            !slice.Slice(2).StartsWith("turday", StringComparison.OrdinalIgnoreCase)) // Match the string "turday" (ordinal case-insensitive)
                        {
                            return false; // The input didn't match.
                        }

                        pos += 8;
                        slice = inputSpan.Slice(pos);
                        break;

                    case 'U' or 'u':
                        if ((uint)slice.Length < 6 ||
                            !slice.Slice(2).StartsWith("nday", StringComparison.OrdinalIgnoreCase)) // Match the string "nday" (ordinal case-insensitive)
                        {
                            return false; // The input didn't match.
                        }

                        pos += 6;
                        slice = inputSpan.Slice(pos);
                        break;

                    default:
                        return false; // The input didn't match.
                }
            }

        }

        AlternationMatch:;
    }

    // The input matched.
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;
}
private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
{
    int pos = base.runtextpos;
    int matchStart = pos;
    ReadOnlySpan<char> slice = inputSpan.Slice(pos);

    // Match with 5 alternative expressions, atomically.
    {
        if (slice.IsEmpty)
        {
            return false; // The input didn't match.
        }

        switch (slice[0])
        {
            case 'M':
                // Match the string "onday".
                if (!slice.Slice(1).StartsWith("onday"))
                {
                    return false; // The input didn't match.
                }

                pos += 6;
                slice = inputSpan.Slice(pos);
                break;

            case 'T':
                // Match with 2 alternative expressions, atomically.
                {
                    if ((uint)slice.Length < 2)
                    {
                        return false; // The input didn't match.
                    }

                    switch (slice[1])
                    {
                        case 'u':
                            // Match the string "esday".
                            if (!slice.Slice(2).StartsWith("esday"))
                            {
                                return false; // The input didn't match.
                            }

                            pos += 7;
                            slice = inputSpan.Slice(pos);
                            break;

                        case 'h':
                            // Match the string "ursday".
                            if (!slice.Slice(2).StartsWith("ursday"))
                            {
                                return false; // The input didn't match.
                            }

                            pos += 8;
                            slice = inputSpan.Slice(pos);
                            break;

                        default:
                            return false; // The input didn't match.
                    }
                }

                break;

            case 'W':
                // Match the string "ednesday".
                if (!slice.Slice(1).StartsWith("ednesday"))
                {
                    return false; // The input didn't match.
                }

                pos += 9;
                slice = inputSpan.Slice(pos);
                break;

            case 'F':
                // Match the string "riday".
                if (!slice.Slice(1).StartsWith("riday"))
                {
                    return false; // The input didn't match.
                }

                pos += 6;
                slice = inputSpan.Slice(pos);
                break;

            case 'S':
                // Match with 2 alternative expressions, atomically.
                {
                    if ((uint)slice.Length < 2)
                    {
                        return false; // The input didn't match.
                    }

                    switch (slice[1])
                    {
                        case 'a':
                            // Match the string "turday".
                            if (!slice.Slice(2).StartsWith("turday"))
                            {
                                return false; // The input didn't match.
                            }

                            pos += 8;
                            slice = inputSpan.Slice(pos);
                            break;

                        case 'u':
                            // Match the string "nday".
                            if (!slice.Slice(2).StartsWith("nday"))
                            {
                                return false; // The input didn't match.
                            }

                            pos += 6;
                            slice = inputSpan.Slice(pos);
                            break;

                        default:
                            return false; // The input didn't match.
                    }
                }

                break;

            default:
                return false; // The input didn't match.
        }
    }

    // The input matched.
    base.runtextpos = pos;
    base.Capture(0, matchStart, pos);
    return true;
}

'nin hemen sonrasında olacak şekilde nasıl Thursday yeniden sıralandığını ve hem çift hem deThursday Tuesday/çift SaturdaySunday/için birden çok anahtar düzeyiyle nasıl sonuçlandığına dikkat edin.Tuesday Aşırı uçta, birçok farklı sözcüğün uzun bir değişimini oluşturursanız, kaynak oluşturucu bir trie^1 mantıksal eşdeğerini yayar, her karakteri okur ve switchsözcüğün geri kalanını işlemek için dalın içine girer. Bu, sözcükleri eşleştirmenin çok verimli bir yoludur ve kaynak oluşturucunun burada yaptığı da budur.

Aynı zamanda kaynak oluşturucunun doğrudan IL'ye çıkış yaparken mevcut olmayan başka sorunları da vardır. Birkaç kod örneğine geri bakarsanız, bazı küme ayraçlarının garip bir şekilde açıklama satırı yaptığını görebilirsiniz. Bu bir hata değil. Kaynak oluşturucu, bu ayraçların açıklama satırı yapılmaması durumunda geri izlemenin yapısının kapsamın dışından bu kapsamın içinde tanımlanan bir etikete atlamak olduğunu fark ediyor; böyle bir etiket için görünür goto olmaz ve kod derlenemez. Bu nedenle, kaynak oluşturucunun bir kapsam olmasını önlemek gerekir. Bazı durumlarda, burada olduğu gibi kapsamı açıklama satırı yapar. Bunun mümkün olmadığı diğer durumlarda, bazen bunun sorunlu olması durumunda kapsam gerektiren yapılardan (çok deyimli if blok gibi) kaçınabilir.

Kaynak oluşturucu, tek bir özel durum dışında her şeyi RegexCompiler işler. İşlemede RegexOptions.IgnoreCaseolduğu gibi, uygulamalar artık yapı zamanında kümeler oluşturmak için bir büyük/küçük harf tablosu kullanır ve geri başvuru eşleştirmesinin bu büyük/küçük harf tablosuna nasıl IgnoreCase başvurması gerekir? Bu tablo , içindedir System.Text.RegularExpressions.dllve şimdilik, en azından bu derlemenin dışındaki kodun (kaynak oluşturucu tarafından yayılan kod dahil) buna erişimi yoktur. Bu, kaynak oluşturucuda geri başvuruları işlemeyi IgnoreCase zorlaştırır ve desteklenmez. Bu, tarafından RegexCompilerdesteklenen kaynak oluşturucu tarafından desteklenmeyen bir yapıdır. Bunlardan birine sahip bir desen kullanmaya çalışırsanız (nadirdir), kaynak oluşturucu özel bir uygulama yaymaz ve bunun yerine normal Regex bir örneği önbelleğe almaya geri döner:

Desteklenmeyen kayıt defteri hala önbelleğe alınıyor

Ayrıca, kaynak RegexCompiler oluşturucu da yeni RegexOptions.NonBacktracking'yi desteklemez. belirtirsenizRegexOptions.Compiled | RegexOptions.NonBacktrackingCompiled, bayrağı yalnızca yoksayılır ve kaynak oluşturucuda belirtirsenizNonBacktracking, benzer şekilde normal Regex bir örneği önbelleğe almaya geri döner.

Kullanılması gereken durumlar

Genel kılavuz, kaynak oluşturucuyu kullanıp kullanamadığınızdır. Bugün C# dilinde derleme zamanında bilinen bağımsız değişkenlerle kullanıyorsanız Regex ve özellikle zaten kullanıyorsanız RegexOptions.Compiled (regex daha hızlı aktarım hızından yararlanabilecek bir etkin nokta olarak tanımlandıysa), kaynak oluşturucuyu kullanmayı tercih etmelisiniz. Kaynak oluşturucu, regex'inize aşağıdaki avantajları sağlar:

  • 'nin tüm aktarım hızı avantajları RegexOptions.Compiled.
  • Tüm regex ayrıştırma, çözümleme ve derleme işlemlerini çalışma zamanında yapmak zorunda olmamasının başlangıç avantajları.
  • Regex için oluşturulan kodla önceden derlemeyi kullanma seçeneği.
  • Daha iyi hata ayıklama ve regex'i anlama.
  • Kırpılan uygulamanızın boyutunu küçültme olanağı, ile RegexCompiler ilişkili büyük kod dalgalarını kırparak (ve hatta yansıma kendini yayar).

Kaynak oluşturucunun özel bir uygulama oluşturamayacağ gibi RegexOptions.NonBacktracking bir seçenekle kullanıldığında, yine de uygulamayı açıklayan önbelleğe alma ve XML açıklamaları yayar ve bu da onu değerli hale getirir. Kaynak oluşturucunun temel dezavantajı, derlemenize ek kod yaymasıdır, bu nedenle boyutun artması olasılığı vardır. Uygulamanızda ne kadar fazla kayıt defteri varsa ve bunlar ne kadar büyük olursa, bunlar için o kadar fazla kod yayılır. Bazı durumlarda, gereksiz olabileceği gibi RegexOptions.Compiled , kaynak oluşturucu da olabilir. Örneğin, yalnızca nadiren gereken ve aktarım hızının önemli olmadığı bir regex'iniz varsa, yalnızca bu düzensiz kullanım için yorumlayıcıya güvenmek daha yararlı olabilir.

Önemli

.NET 7, kaynak oluşturucuya dönüştürülebilecek kullanımını Regex tanımlayan bir çözümleyici ve sizin için dönüştürmeyi sağlayan bir çözümleyici içerir:

RegexGenerator çözümleyicisi ve düzelticisi

Ayrıca bkz.