.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 Regex
statik 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.Compiled
vardı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 Regex
kı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 Regex
bir 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:
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 Regex
tü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ığı CompileToAssembly
olmadan 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.
İ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.
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|Sunday
iç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 Saturday
Sunday
/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 switch
sö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.IgnoreCase
olduğ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.dll
ve ş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 RegexCompiler
desteklenen 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:
Ayrıca, kaynak RegexCompiler
oluşturucu da yeni RegexOptions.NonBacktracking
'yi desteklemez. belirtirsenizRegexOptions.Compiled | RegexOptions.NonBacktracking
Compiled
, 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: