Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu öğreticide şunların nasıl yapılacağını öğreneceksiniz:
- Dize ilişkilendirme işleyicisi desenini uygulayın.
- Bir dize ilişkilendirme işleminde alıcıyla etkileşime geçin.
- Dize ilişkilendirme işleyicisine bağımsız değişkenler ekleyin.
- Dize ilişkilendirme için yeni kitaplık özelliklerini anlama.
Önkoşullar
Makinenizi .NET çalıştıracak şekilde ayarlayın. C# derleyicisi Visual Studio veya .NET SDK aracılığıyla kullanılabilir.
Bu öğreticide, Visual Studio veya Visual Studio Code ve C# DevKit dahil olmak üzere C# ve .NET hakkında bilgi sahibi olduğunuz varsayılır.
özel olarak ilişkilendirilmiş dize işleyicisi yazabilirsiniz. İnterpolasyonlu dize işleyicisi, bir interpolasyonlu dizedeki yer tutucu ifadeyi işleyen bir türdür. Özel işleyici olmadan, sistem benzeri String.Formatyer tutucuları işler. Her yer tutucu metin olarak biçimlendirilir ve ardından bileşenler sonuçta elde edilen dizeyi oluşturacak şekilde birleştirilir.
Sonuçta elde edilen dize hakkındaki bilgileri kullandığınız herhangi bir senaryo için bir işleyici yazabilirsiniz. Şu soruları göz önünde bulundurun: Kullanılıyor mu? Biçimde hangi kısıtlamalar var? Bazı örnekler şunlardır:
- Sonuçta elde edilen dizelerin hiçbirinin 80 karakter gibi bir sınırdan büyük olmaması gerekebilir. Sabit uzunlukta bir arabelleği doldurmak için ilişkilendirilmiş dizeleri işleyebilir ve arabellek uzunluğuna ulaşıldıktan sonra işlemeyi durdurabilirsiniz.
- Tablosal bir biçiminiz olabilir ve her yer tutucu sabit uzunlukta olmalıdır. Özel işleyici, tüm istemci kodunu uyumlu olmaya zorlamak yerine bu kısıtlamayı zorunlu kılabilir.
Bu öğreticide, temel performans senaryolarından biri için bir dize ilişkilendirme işleyicisi oluşturacaksınız: günlük kitaplıkları. Yapılandırılan günlük düzeyine bağlı olarak, günlük iletisi oluşturma işi gerekmeyebilir. Loglama kapalıysa, ara eklenmiş bir dize ifadesinden bir dize oluşturma çalışması gerekli değildir. Mesaj hiçbir zaman yazdırılmayacağı için herhangi bir dize birleştirme atlanabilir. Ayrıca, yığın izlemeleri oluşturma da dahil olmak üzere yer tutucularda kullanılan herhangi bir ifadenin yapılmasına gerek yoktur.
Interpolasyonlu dize işleyicisi, biçimlendirilmiş dizenin kullanılıp kullanılmadığını belirleyebilir ve yalnızca gerekirse gerekli çalışmayı gerçekleştirebilir.
İlk uygulama
Farklı düzeyleri destekleyen temel Logger bir sınıfla başlayın:
public enum LogLevel
{
Off,
Critical,
Error,
Warning,
Information,
Trace
}
public class Logger
{
public LogLevel EnabledLevel { get; init; } = LogLevel.Error;
public void LogMessage(LogLevel level, string msg)
{
if (EnabledLevel < level) return;
Console.WriteLine(msg);
}
}
Bu Logger altı farklı düzeyi destekler. İleti günlük düzeyi filtresini geçmezse, kayıtlayıcı herhangi bir çıktı üretmez. Kayıt tutucu için genel API, mesaj olarak tamamen biçimlendirilmiş bir dize kabul eder. Çağıran, dizeyi oluşturmak için tüm işi yapar.
İşleyici desenini uygulama
Bu adımda, geçerli davranışı yeniden oluşturan bir ilişkilendirilmiş dize işleyicisi oluşturursunuz. Gömülü dize işleyicisi, aşağıdaki özelliklere sahip olması gereken bir türdür:
- Türe uygulanan System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute.
-
intveliteralLengtholmak üzere ikiformattedCountparametresi olan bir oluşturucu. (Daha fazla parametreye izin verilir). - Açık bir
AppendLiteralmetodu, imzası:public void AppendLiteral(string s). - İmzası olan genel bir genel
AppendFormattedyöntemi:public void AppendFormatted<T>(T t).
Oluşturucu, biçimlendirilmiş dizeyi dahili olarak oluşturur ve bir istemcinin bu dizeyi alması için bir üye sağlar. Aşağıdaki kod, bu gereksinimleri karşılayan bir LogInterpolatedStringHandler türünü gösterir:
[InterpolatedStringHandler]
public struct LogInterpolatedStringHandler
{
// Storage for the built-up string
StringBuilder builder;
public LogInterpolatedStringHandler(int literalLength, int formattedCount)
{
builder = new StringBuilder(literalLength);
Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
}
public void AppendLiteral(string s)
{
Console.WriteLine($"\tAppendLiteral called: {{{s}}}");
builder.Append(s);
Console.WriteLine($"\tAppended the literal string");
}
public void AppendFormatted<T>(T t)
{
Console.WriteLine($"\tAppendFormatted called: {{{t}}} is of type {typeof(T)}");
builder.Append(t?.ToString());
Console.WriteLine($"\tAppended the formatted object");
}
public override string ToString() => builder.ToString();
}
Uyarı
İlişkili dize ifadesi bir derleme zamanı sabiti olduğunda (yani yer tutucusu olmadığında), derleyici özel bir ilişkilendirilmiş dize işleyicisi çağırmak yerine hedef türü string kullanır. Bu davranış, sabit interpolate edilmiş dizelerin özel işleyicileri tamamen atlayarak işlev görmesi anlamına gelir.
Artık yeni interpolasyonlu dize işleyicinizi denemek için LogMessage sınıfındaki Logger overload'ını ekleyebilirsiniz:
public void LogMessage(LogLevel level, LogInterpolatedStringHandler builder)
{
if (EnabledLevel < level) return;
Console.WriteLine(builder.ToString());
}
Özgün LogMessage yöntemi kaldırmanız gerekmez. Bağımsız değişken bir interpolasyonlu dize ifadesi olduğunda, derleyici bir interpolasyon işleyici parametresine sahip bir yöntemi, bir string parametreli yönteme tercih eder.
Yeni işleyicinin çağrıldığını doğrulamak için ana program olarak aşağıdaki kodu kullanabilirsiniz:
var logger = new Logger() { EnabledLevel = LogLevel.Warning };
var time = DateTime.Now;
logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time}. This is an error. It will be printed.");
logger.LogMessage(LogLevel.Trace, $"Trace Level. CurrentTime: {time}. This won't be printed.");
logger.LogMessage(LogLevel.Warning, "Warning Level. This warning is a string, not an interpolated string expression.");
Uygulamanın çalıştırılması aşağıdaki metne benzer bir çıkış oluşturur:
literal length: 65, formattedCount: 1
AppendLiteral called: {Error Level. CurrentTime: }
Appended the literal string
AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
Appended the formatted object
AppendLiteral called: {. This is an error. It will be printed.}
Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. This is an error. It will be printed.
literal length: 50, formattedCount: 1
AppendLiteral called: {Trace Level. CurrentTime: }
Appended the literal string
AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
Appended the formatted object
AppendLiteral called: {. This won't be printed.}
Appended the literal string
Warning Level. This warning is a string, not an interpolated string expression.
Çıktıyı izlediğinizde, derleyicinin işleyiciyi çağırmak ve dizeyi derlemek için nasıl kod eklediğini görebilirsiniz:
- Derleyici, biçim dizesindeki değişmez metnin toplam uzunluğunu ve yer tutucu sayısını geçirerek işleyiciyi oluşturmak için bir çağrı ekler.
- Derleyici, literal dizgenin her bölümü ve her yer tutucu için
AppendLiteralveAppendFormattedçağrıları ekler. - Derleyici, bağımsız değişken olarak
LogMessage'i kullanarakCoreInterpolatedStringHandleryöntemini çağırır.
Son olarak, son uyarının ilişkilendirilmiş dize işleyicisini çağırmadığını fark edin. Bağımsız değişken bir stringolduğundan, bu çağrı bir dize parametresiyle diğer aşırı yüklemeyi tetikler.
Önemli
yalnızca kesinlikle gerekliyse, ilişkilendirilmiş dize işleyicileri için ref struct kullanın.
ref struct türlerin yığında depolanması gerektiğinden sınırlamaları vardır. Örneğin, bir ilişkilendirilmiş dize deliği bir await ifade içeriyorsa, derleyicinin işleyiciyi derleyici tarafından oluşturulan IAsyncStateMachine uygulamada depolaması gerektiğinden çalışmazlar.
İşleyiciye daha fazla özellik ekleme
Interpolasyonlu dize işleyicisinin önceki sürümü deseni uygular. Her yer tutucu ifadenin işlenmesini önlemek için işleyicide daha fazla bilgiye ihtiyacınız vardır. Bu bölümde, işleyicinizi geliştirerek, oluşturduğunuz dize günlüğe yazılmadığında daha az çalışmasını sağlayın. System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute kullanarak genel API'ye parametreler ve işleyicinin oluşturucusuna parametreler arasında eşleme yaparsınız. Bu eşleme, işleyiciye, ilişkilendirilmiş dizenin değerlendirilmesinin gerekip gerekmediğini belirlemek için gereken bilgileri sağlar.
İşleyici fonksiyondaki değişikliklerle başlayın. İlk olarak, işleyicinin etkin olup olmadığını izlemek için bir alan ekleyin. Oluşturucuya iki parametre ekleyin: biri, bu iletinin günlük düzeyini belirtmek için; diğeri ise günlük nesnesine referans olarak.
private readonly bool enabled;
public LogInterpolatedStringHandler(int literalLength, int formattedCount, Logger logger, LogLevel logLevel)
{
enabled = logger.EnabledLevel >= logLevel;
builder = new StringBuilder(literalLength);
Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
}
Ardından, işlecinizin yalnızca nihai dize kullanıldığında sabit değerleri veya biçimlendirilmiş nesneleri eklemesi için alanı kullanın.
public void AppendLiteral(string s)
{
Console.WriteLine($"\tAppendLiteral called: {{{s}}}");
if (!enabled) return;
builder.Append(s);
Console.WriteLine($"\tAppended the literal string");
}
public void AppendFormatted<T>(T t)
{
Console.WriteLine($"\tAppendFormatted called: {{{t}}} is of type {typeof(T)}");
if (!enabled) return;
builder.Append(t?.ToString());
Console.WriteLine($"\tAppended the formatted object");
}
Ardından, derleyicinin LogMessage ek parametreleri işleyicinin oluşturucusuna geçirmesi için bildirimini güncelleyin. İşleyici bağımsız değişkeni üzerinde System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute kullanarak bu adımı gerçekleştirin.
public void LogMessage(LogLevel level, [InterpolatedStringHandlerArgument("", "level")] LogInterpolatedStringHandler builder)
{
if (EnabledLevel < level) return;
Console.WriteLine(builder.ToString());
}
Bu öznitelik, gerekli LogMessage ve literalLength parametrelerinden sonraki parametrelere uygun formattedCount için bağımsız değişkenlerin listesini belirtir. Boş dize ()"" alıcıyı belirtir. Derleyici, işleyici oluşturucusunun sonraki bağımsız değişkenine Logger tarafından temsil edilen this nesnesinin değerini atar. Derleyici, aşağıdaki bağımsız değişken için level değerini kullanır. Yazdığınız herhangi bir işleyici için istediğiniz sayıda bağımsız değişken sağlayabilirsiniz. Eklediğiniz bağımsız değişkenler dize bağımsız değişkenleridir.
Uyarı
InterpolatedStringHandlerArgumentAttribute Oluşturucu bağımsız değişken listesi boşsa, davranış özniteliğin tamamen atlanmış olmasıyla aynıdır.
Aynı test kodunu kullanarak bu sürümü çalıştırabilirsiniz. Bu kez aşağıdaki sonuçları görürsünüz:
literal length: 65, formattedCount: 1
AppendLiteral called: {Error Level. CurrentTime: }
Appended the literal string
AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
Appended the formatted object
AppendLiteral called: {. This is an error. It will be printed.}
Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. This is an error. It will be printed.
literal length: 50, formattedCount: 1
AppendLiteral called: {Trace Level. CurrentTime: }
AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
AppendLiteral called: {. This won't be printed.}
Warning Level. This warning is a string, not an interpolated string expression.
AppendLiteral ve AppendFormat yöntemlerinin çağrıldığını, ancak herhangi bir iş yapmadıklarını gördüğünüzü görürsünüz. İşleyici, son dizenin gerekli olmadığını belirledi, bu nedenle işleyici bunu derlemez. Yine de yapılması gereken birkaç geliştirme vardır.
İlk olarak, bağımsız değişkeni AppendFormatteduygulayan bir türle kısıtlayan bir System.IFormattable aşırı yüklemesi ekleyebilirsiniz. Bu aşırı yükleme, çağıranların yer tutuculara biçim dizeleri eklemesini sağlar. Bu değişikliği yaparken, diğer AppendFormatted ve AppendLiteral yöntemlerinin dönüş türünü voidbool olarak değiştirin. Bu yöntemlerden herhangi biri farklı dönüş türlerine sahipse bir derleme hatası alırsınız. Bu değişiklik içinkısa devreyi etkinleştirir. Yöntemler, ilişkilendirilmiş dize ifadesinin işlenmesinin durdurulması gerektiğini belirtmek için false döndürür.
true döndürülmesi, devam etmesi gerektiğini gösterir. Bu örnekte, sonuçta elde edilen dize gerekli olmadığında işlemeyi durdurmak için bunu kullanıyorsunuz. Kısa devre daha ayrıntılı eylemleri destekler. Belirli bir uzunluğa ulaştıklarında ifadelerin işlenmesini durdurarak sabit uzunlukta arabellekleri destekleyebilirsiniz. Veya bazı koşullar kalan öğelerin gerekli olmadığını gösterebilir.
public void AppendFormatted<T>(T t, string format) where T : IFormattable
{
Console.WriteLine($"\tAppendFormatted (IFormattable version) called: {t} with format {{{format}}} is of type {typeof(T)},");
builder.Append(t?.ToString(format, null));
Console.WriteLine($"\tAppended the formatted object");
}
public void AppendFormatted<T>(T t, int alignment, string format) where T : IFormattable
{
Console.WriteLine($"\tAppendFormatted (IFormattable version) called: {t} with alignment {alignment} and format {{{format}}} is of type {typeof(T)},");
var formatString =$"{alignment}:{format}";
builder.Append(string.Format($"{{0,{formatString}}}", t));
Console.WriteLine($"\tAppended the formatted object");
}
Bu eklemeyle, ilişkilendirilmiş dize ifadenizde biçim dizeleri belirtebilirsiniz:
var time = DateTime.Now;
logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time}. The time doesn't use formatting.");
logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time:t}. This is an error. It will be printed.");
logger.LogMessage(LogLevel.Trace, $"Trace Level. CurrentTime: {time:t}. This won't be printed.");
İlk iletideki :t geçerli saat için "kısa saat biçimini" belirtir. Önceki örnekte, işleyiciniz için oluşturabileceğiniz AppendFormatted yöntemine yönelik aşırı yüklemelerden biri gösterildi. Biçimlendirilen nesne için genel bir bağımsız değişken belirtmeniz gerekmez. Oluşturduğunuz türleri dizeye dönüştürmek için daha verimli yöntemler bulunabilir. Genel bağımsız değişken yerine bu türleri alan AppendFormatted aşırı yüklemeleri yazabilirsiniz. Derleyici en iyi aşırı yüklemeyi seçer. Çalışma zamanı, System.Span<T>'ı metin çıktısına dönüştürmek için bu tekniği kullanır.
ile veya olmadan çıktının IFormattable belirtmek için bir tamsayı parametresi ekleyebilirsiniz. .NET 6 ile birlikte gelen System.Runtime.CompilerServices.DefaultInterpolatedStringHandler, farklı kullanımlar için dokuz AppendFormatted aşırı yüklemesi içerir. Bunu, amaçlarınıza uygun bir işleyici oluştururken başvuru olarak kullanabilirsiniz.
Örneği şimdi çalıştırdığınızda Trace iletisi için yalnızca ilk AppendLiteral çağrıldığını görürsünüz:
literal length: 60, formattedCount: 1
AppendLiteral called: Error Level. CurrentTime:
Appended the literal string
AppendFormatted called: 10/20/2021 12:18:29 PM is of type System.DateTime
Appended the formatted object
AppendLiteral called: . The time doesn't use formatting.
Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:18:29 PM. The time doesn't use formatting.
literal length: 65, formattedCount: 1
AppendLiteral called: Error Level. CurrentTime:
Appended the literal string
AppendFormatted (IFormattable version) called: 10/20/2021 12:18:29 PM with format {t} is of type System.DateTime,
Appended the formatted object
AppendLiteral called: . This is an error. It will be printed.
Appended the literal string
Error Level. CurrentTime: 12:18 PM. This is an error. It will be printed.
literal length: 50, formattedCount: 1
AppendLiteral called: Trace Level. CurrentTime:
Warning Level. This warning is a string, not an interpolated string expression.
İşleyicinin oluşturucusunun verimliliğini artıran son bir güncelleştirme yapabilirsiniz. İşleyici son bir out bool parametresi ekleyebilir. Bu parametreyi false olarak ayarlamak, ilişkilendirilmiş dize ifadesini işlemek için işleyicinin hiç çağrılmaması gerektiğini gösterir:
public LogInterpolatedStringHandler(int literalLength, int formattedCount, Logger logger, LogLevel level, out bool isEnabled)
{
isEnabled = logger.EnabledLevel >= level;
Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
builder = isEnabled ? new StringBuilder(literalLength) : default!;
}
Bu değişiklik, enabled alanını kaldırabileceğiniz anlamına gelir. Ardından, AppendLiteral ve AppendFormatted dönüş türünü voidolarak değiştirebilirsiniz.
Şimdi örneği çalıştırdığınızda aşağıdaki çıkışı görürsünüz:
literal length: 60, formattedCount: 1
AppendLiteral called: Error Level. CurrentTime:
Appended the literal string
AppendFormatted called: 10/20/2021 12:19:10 PM is of type System.DateTime
Appended the formatted object
AppendLiteral called: . The time doesn't use formatting.
Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. The time doesn't use formatting.
literal length: 65, formattedCount: 1
AppendLiteral called: Error Level. CurrentTime:
Appended the literal string
AppendFormatted (IFormattable version) called: 10/20/2021 12:19:10 PM with format {t} is of type System.DateTime,
Appended the formatted object
AppendLiteral called: . This is an error. It will be printed.
Appended the literal string
Error Level. CurrentTime: 12:19 PM. This is an error. It will be printed.
literal length: 50, formattedCount: 1
Warning Level. This warning is a string, not an interpolated string expression.
LogLevel.Trace belirtildiğinde tek çıkış oluşturucudan gelen çıkıştır. İşleyici etkin olmadığını belirttiği için Append yöntemlerinden hiçbiri çağrılmıyor.
Bu örnekte, özellikle günlük kitaplıkları kullanılırken, ilişkilendirilmiş dize işleyicileri için önemli bir nokta gösterilmektedir. Yer tutuculardaki yan etkiler oluşmayabilir. Ana programınıza aşağıdaki kodu ekleyin ve bu davranışı çalışırken görün:
int index = 0;
int numberOfIncrements = 0;
for (var level = LogLevel.Critical; level <= LogLevel.Trace; level++)
{
Console.WriteLine(level);
logger.LogMessage(level, $"{level}: Increment index {index++}");
numberOfIncrements++;
}
Console.WriteLine($"Value of index {index}, value of numberOfIncrements: {numberOfIncrements}");
Döngünün her yinelemesinde index değişkeninin artırıldığını görebilirsiniz. Yer tutucular yalnızca Critical, Error ve Warning düzeyleri için değerlendirilip Information ve Trace için değerlendirilmediğinden, index'in son değeri beklentiyle eşleşmiyor.
Critical
Critical: Increment index 0
Error
Error: Increment index 1
Warning
Warning: Increment index 2
Information
Trace
Value of index 3, value of numberOfIncrements: 5
İlişkili dize işleyicileri, ilişkilendirilmiş dize ifadesinin bir dizeye nasıl dönüştürüldüğü üzerinde daha fazla denetim sağlar. .NET çalışma zamanı ekibi bu özelliği çeşitli alanlarda performansı geliştirmek için kullandı. Kendi kitaplıklarınızda aynı özelliği kullanabilirsiniz. Daha fazla araştırmak için System.Runtime.CompilerServices.DefaultInterpolatedStringHandlerbakın. Burada oluşturduğunuzdan daha eksiksiz bir uygulama sağlar.
Append yöntemleri için mümkün olan çok daha fazla aşırı yükleme görürsünüz.