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.
Not
Bu makale bir özellik belirtimidir. Belirtim, özelliğin tasarım belgesi olarak görev alır. Önerilen belirtim değişikliklerini ve özelliğin tasarımı ve geliştirilmesi sırasında gereken bilgileri içerir. Bu makaleler, önerilen belirtim değişiklikleri son haline getirilene ve geçerli ECMA belirtimine dahil edilene kadar yayımlanır.
Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar, ilgili dil tasarım toplantısı (LDM) notlarındakaydedilir.
Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek için belirtimleri makalesinde bulabilirsiniz.
Şampiyon sorunu: https://github.com/dotnet/csharplang/issues/4487
Özet
Hem genel string senaryolarında hem de günlüğe kaydetme çerçeveleri gibi daha özel senaryolarda, dizeyi çerçevede biçimlendirmeye gerek kalmadan verimli biçimlendirmeye ve kullanıma olanak sağlamak için ilişkilendirilmiş dize ifadeleri oluşturmaya ve kullanmaya yönelik yeni bir desen ekledik.
Motivasyon
Bugün, dize ilişkilendirmesi çoğunlukla string.Formatçağrısına iner. Bu, genel amaçlı olsa da çeşitli nedenlerle verimsiz olabilir:
- Çalışma zamanı tam olarak doğru bağımsız değişken türlerini tam olarak doğru sırada alan bir
string.Formataşırı yüklemesi oluşturmadığı sürece tüm yapı bağımsız değişkenlerini kutular.- Bu sıralama, çalışma zamanının yöntemin genel sürümlerini tanıtmakta tereddütlü olmasının nedenidir, çünkü çok yaygın bir yöntemin genel örneklemelerinin kombinatorik patlamasına yol açabilir.
- Çoğu durumda bağımsız değişkenler için bir dizi ayırması gerekir.
- Gerekli değilse örneği örneklemekten kaçınma fırsatı yoktur. Örneğin, günlükleme çerçeveleri, uygulamanın geçerli log seviyesine bağlı olarak gerekli olmayabilecek bir dizenin oluşturulmasına neden olacağı için dize interpolasyonundan kaçınmanızı önerir.
- Başvuru yapılarına genel tür parametreleri olarak izin verilmediğinden,
Spanveya diğer başvuru yapısı türlerini bugün hiçbir zaman kullanamaz; başka bir deyişle, kullanıcı ara konumlara kopyalamaktan kaçınmak isterse dizeleri el ile biçimlendirmek zorunda olur.
Çalışma zamanı, bu senaryoların ilk 2 senaryoyla ilgilenmeye yardımcı olmak için dahili olarak ValueStringBuilder adında bir türe sahiptir. Oluşturucuya bir stackalloc'd arabelleği geçirir, her bölümü AppendFormat tekrar tekrar çağırır ve son dizeyi alır. Sonuçta elde edilen dize yığın arabelleğinin sınırlarını geçerse yığındaki bir diziye geçebilir. Ancak, bu türün doğrudan ortaya çıkarılması tehlikelidir, çünkü yanlış kullanım kiralanmış bir dizinin iki kez serbest bırakılmasına yol açabilir, bu da iki konumun kiralanmış diziye yalnızca kendilerinin erişim hakkı olduğunu düşünmesi nedeniyle programda her türlü tanımsız davranışa neden olur. Bu teklif, yalnızca interpolasyonlu bir dize sabiti yazarak yerel C# kodunda bu türü güvenli bir şekilde kullanmanın bir yolunu oluşturur ve kullanıcı tarafından yazılan her interpolasyonlu dizeyi geliştirirken yazılmış kodu değiştirmez. Bu desen ayrıca, interpolasyonlu dizelerin diğer yöntemlere bağımsız değişken olarak geçerken, yöntemin alıcısı tarafından tanımlanan bir işleyici deseni kullanmasına olanak tanıyacak şekilde genişletilir. Bu düzen, günlüğe kaydetme çerçeveleri gibi bileşenlerin asla gerekmeyecek dizeleri ayırmasını önler ve C# kullanıcılarına tanıdık, kullanımı kolay bir interpolasyon söz dizimi sunar.
Ayrıntılı Tasarım
İşleyici Deseni
Yönteme bağımsız değişken olarak iletilen ara diziyi temsil edebilen yeni bir işleyici deseni tanıtıyoruz. Desenin basit İngilizcesi aşağıdaki gibidir:
Bir interpolated_string_expression bir metot için bağımsız değişken olarak verildiğinde, parametrenin türüne bakarız. Parametre türünün, literalLength ve formattedCountolmak üzere 2 tamsayı parametresiyle çağrılabilen bir oluşturucuya sahip olduğu, özgün parametredeki bir öznitelik tarafından belirtilen ek parametreleri opsiyonel olarak alabildiği, ayrıca isteğe bağlı bir çıkış boole tipi sondaki parametresi olduğu ve özgün parametrenin türünün interpolasyon dizesinin her bölümünde çağrılabilen örnek AppendLiteral ve AppendFormatted yöntemlerine sahip olduğu durumlarda, interpolasyonu geleneksel bir çağrı yerine string.Format(formatStr, args)ile çağrı yaparak daha alt seviyeye indiririz. Bunu hayal etme konusunda daha somut bir örnek yararlı olacaktır:
// The handler that will actually "build" the interpolated string"
[InterpolatedStringHandler]
public ref struct TraceLoggerParamsInterpolatedStringHandler
{
// Storage for the built-up string
private bool _logLevelEnabled;
public TraceLoggerParamsInterpolatedStringHandler(int literalLength, int formattedCount, Logger logger, out bool handlerIsValid)
{
if (!logger._logLevelEnabled)
{
handlerIsValid = false;
return;
}
handlerIsValid = true;
_logLevelEnabled = logger.EnabledLevel;
}
public void AppendLiteral(string s)
{
// Store and format part as required
}
public void AppendFormatted<T>(T t)
{
// Store and format part as required
}
}
// The logger class. The user has an instance of this, accesses it via static state, or some other access
// mechanism
public class Logger
{
// Initialization code omitted
public LogLevel EnabledLevel;
public void LogTrace([InterpolatedStringHandlerArguments("")]TraceLoggerParamsInterpolatedStringHandler handler)
{
// Impl of logging
}
}
Logger logger = GetLogger(LogLevel.Info);
// Given the above definitions, usage looks like this:
var name = "Fred Silberberg";
logger.LogTrace($"{name} will never be printed because info is < trace!");
// This is converted to:
var name = "Fred Silberberg";
var receiverTemp = logger;
var handler = new TraceLoggerParamsInterpolatedStringHandler(literalLength: 47, formattedCount: 1, receiverTemp, out var handlerIsValid);
if (handlerIsValid)
{
handler.AppendFormatted(name);
handler.AppendLiteral(" will never be printed because info is < trace!");
}
receiverTemp.LogTrace(handler);
Burada, TraceLoggerParamsInterpolatedStringHandler doğru parametrelere sahip bir oluşturucuya sahip olduğundan, interpolasyon dizisinin bu parametreye örtük bir işleyici dönüşümüne sahip olduğunu ve yukarıda gösterilen desene indirildiğini söyleriz. Bunun için gereken spesifikasyon biraz karmaşıktır ve aşağıda detaylandırılmıştır.
Bu teklifin geri kalanında, her ikisinin de geçerli olduğu durumlarda Append... veya AppendLiteral'ye atıfta bulunmak için AppendFormatted kullanılacaktır.
Yeni öznitelikler
Derleyici System.Runtime.CompilerServices.InterpolatedStringHandlerAttributetanır:
using System;
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerAttribute : Attribute
{
public InterpolatedStringHandlerAttribute()
{
}
}
}
Bu öznitelik, bir türün geçerli bir ilişkilendirilmiş dize işleyici türü olup olmadığını belirlemek için derleyici tarafından kullanılır.
Derleyici ayrıca System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttributetanır:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public InterpolatedHandlerArgumentAttribute(string argument);
public InterpolatedHandlerArgumentAttribute(params string[] arguments);
public string[] Arguments { get; }
}
}
Bu öznitelik, derleyiciye parametre konumunda kullanılan ilişkilendirilmiş dize işleyici desenini nasıl düşüreceklerini bildirmek için parametrelerde kullanılır.
Interpolasyonlu dize işleyici dönüştürme
T türünün, niteliğiyle donatıldığında System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute olduğu söylenir.
Tamamen _interpolated_string_expression_‘lardan oluşan ve sadece işleçlerini kullanan bir T veya bir interpolated_string_expressionile ilgili olarak, bir interpolated_string_handler_conversion şeklinde bir + dönüşümü vardır.
Bu belirtimin geri kalanında kolaylık olması için, interpolated_string_expression hem basit bir interpolated_string_expressionhem de tamamen _interpolated_string_expression_s oluşan ve yalnızca işleçleri kullanan bir + ifade eder.
İşleyici desenini kullanarak ilişkilendirmeyi düşürmeye çalışırken daha sonra hatalar olup olmayacağına bakılmaksızın, bu dönüştürmenin her zaman var olduğunu unutmayın. Bu, tahmin edilebilir ve yararlı hatalar olduğundan ve çalışma zamanı davranışının ilişkilendirilmiş dizenin içeriğine göre değişmediğinden emin olmak için yapılır.
Uygulanabilir işlev üyesi ayarlamaları
İlgili işlev üyesi algoritmasının ifadesini (§12.6.4.2) şu şekilde düzenliyoruz: her bir bölüme kalın yazıyla yeni bir alt madde işareti eklenir.
İşlev üyesinin, aşağıdakilerin tümü doğru olduğunda bağımsız değişken listesi işlev üyesi olduğu söylenir:
-
A'daki her bağımsız değişken, karşılık gelen parametreler (§12.6.2.2) bölümünde açıklandığı gibi işlev üyesi bildirimindeki bir parametreye karşılık gelir ve bağımsız değişkenin karşılık olmadığı tüm parametreler isteğe bağlı bir parametredir. -
Aiçindeki her bağımsız değişken için bağımsız değişkenin parametre geçirme modu (değer,refveyaout) ilgili parametrenin parametre geçirme moduyla aynıdır ve- bir değer parametresi veya parametre dizisi için, bağımsız değişkenin ilgili parametrenin türüne örtük olarak dönüştürüldüğü bir durum (§10.2) söz konusudur veya
- türü yapı türü, bağımsız değişkenden ilgili parametrenin türüne kadar örtük bir
refbulunan bir parametresi için veya -
refveyaoutparametresi için bağımsız değişkenin türü, karşılık gelen parametrenin türüyle aynıdır. Sonuçta,refveyaoutparametresi geçirilen bağımsız değişkenin diğer adıdır.
Parametre dizisi içeren bir işlev üyesi için, işlev üyesi yukarıdaki kurallar tarafından uygulanıyorsa,normal formunda geçerli olduğu söylenir. Parametre dizisi içeren bir işlev üyesi normal biçiminde uygulanamazsa, işlev üyesi bunun yerine genişletilmiş formuuygulanabilir:
- Genişletilmiş form, işlev üyesi bildirimindeki parametre dizisi, parametre dizisinin öğe türündeki sıfır veya daha fazla değer parametresiyle değiştirilerek oluşturulur, böylece bağımsız değişken listesindeki bağımsız değişken sayısı
Atoplam parametre sayısıyla eşleşir.A, işlev üyesi bildirimindeki sabit parametre sayısından daha az bağımsız değişkene sahipse, işlev üyesinin genişletilmiş biçimi oluşturulamaz ve bu nedenle geçerli değildir. - Aksi takdirde, genişletilmiş form, her bağımsız değişkenin
A'daki parametre geçirme modu, ilgili parametrenin parametre geçirme moduyla aynıysa geçerlidir ve- bir sabit değer parametresi veya genişletme tarafından oluşturulan bir değer parametresi için, bağımsız değişkenin türünden ilgili parametrenin türüne kadar örtük bir dönüştürme (§10.2) vardır veya
- türü yapı türü, bağımsız değişkenden ilgili parametrenin türüne kadar örtük bir
refbulunan bir parametresi için veya -
refveyaoutparametresi için bağımsız değişkenin türü, karşılık gelen parametrenin türüyle aynıdır.
Önemli not: Bu, yalnızca applicable_interpolated_string_handler_typetürüne göre farklılık gösteren 2 eşdeğer aşırı yükleme varsa, bu aşırı yüklemelerin belirsiz olarak kabul edileceği anlamına gelir. Ayrıca, açık atamalar aracılığıyla görmediğimiz için, her iki geçerli aşırı yüklemenin de InterpolatedStringHandlerArguments kullandığı ve işleyici alt seviyeye indirme modelini el ile gerçekleştirmeden çağrılamaz olduğu çözümlenemez bir senaryo ortaya çıkabilir. Bunu seçtiğimizde çözmek için daha iyi işlev üyesi algoritmasında değişiklikler yapabiliriz, ancak bu senaryonun gerçekleşme olasılığı düşüktür ve ele alınması gereken bir öncelik değildir.
İfade ayarlarından daha iyi bir dönüşüm
İfade (§12.6.4.5) bölümünden daha iyi dönüştürmeyi aşağıdaki şekilde değiştiririz:
C1 ifadesi E'dan T1türüne bir örtük dönüştürme ve C2 ifadesini Etürüne dönüştüren örtük dönüştürme T2 göz önünde bulundurulduğunda, C1'e göre daha iyi bir 'dur:
-
Esabit olmayan bir interpolated_string_expression,C1bir implicit_string_handler_conversion,T1bir applicable_interpolated_string_handler_typeveC2bir implicit_string_handler_conversiondeğil veya -
ET2tam olarak eşleşmez ve aşağıdakilerden en az biri geçerlidir:
Bu, söz konusu iç içe geçmiş dizenin sabit bir ifade olup olmamasına bağlı olarak, açıkça anlaşılmayan bazı aşırı yükleme çözümleme kuralları olduğu anlamına gelir. Mesela:
void Log(string s) { ... }
void Log(TraceLoggerParamsInterpolatedStringHandler p) { ... }
Log($""); // Calls Log(string s), because $"" is a constant expression
Log($"{"test"}"); // Calls Log(string s), because $"{"test"}" is a constant expression
Log($"{1}"); // Calls Log(TraceLoggerParamsInterpolatedStringHandler p), because $"{1}" is not a constant expression
Bu, sabit olarak yayılabilen şeylerin bunu yapması ve ek yüke neden olmazken sabit olamayan öğelerin işleyici desenini kullanması için kullanıma sunulmuştur.
InterpolatedStringHandler ve Kullanım
System.Runtime.CompilerServicesyeni bir tür tanıtıyoruz: DefaultInterpolatedStringHandler. Bu, C# derleyicisi tarafından doğrudan kullanıma yönelik ValueStringBuilderile aynı semantiğin birçoğuna sahip bir başvuru yapısıdır. Bu yapı yaklaşık olarak şöyle görünür:
// API Proposal issue: https://github.com/dotnet/runtime/issues/50601
namespace System.Runtime.CompilerServices
{
[InterpolatedStringHandler]
public ref struct DefaultInterpolatedStringHandler
{
public DefaultInterpolatedStringHandler(int literalLength, int formattedCount);
public string ToStringAndClear();
public void AppendLiteral(string value);
public void AppendFormatted<T>(T value);
public void AppendFormatted<T>(T value, string? format);
public void AppendFormatted<T>(T value, int alignment);
public void AppendFormatted<T>(T value, int alignment, string? format);
public void AppendFormatted(ReadOnlySpan<char> value);
public void AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null);
public void AppendFormatted(string? value);
public void AppendFormatted(string? value, int alignment = 0, string? format = null);
public void AppendFormatted(object? value, int alignment = 0, string? format = null);
}
}
Bir interpolated_string_expression (§12.8.3) anlamı için kurallarda küçük bir değişiklik yapıyoruz:
İlişkili dizenin türü string ve tür System.Runtime.CompilerServices.DefaultInterpolatedStringHandler varsa ve geçerli bağlam bu tür kullanmayı destekliyorsa, dizeişleyici deseni kullanılarak indirilir. Son string değeri, işleyici türündeki ToStringAndClear() çağrılarak elde edilir.Aksi takdirde, ilişkilendirilmiş dizenin türü System.IFormattable veya System.FormattableString ise [gerisi değişmez]
"ve geçerli bağlam bu türün kullanılmasını destekliyor" kuralı, derleyiciye bu düzenin kullanımını iyileştirme konusunda yol vermek için kasıtlı olarak belirsizdir. İşleyici türü büyük olasılıkla bir referans yapısı türüdür ve referans yapısı türlerine normalde zaman uyumsuz yöntemlerde izin verilmez. Bu özel durumda, ilişkilendirme deliklerinden hiçbiri bir await ifadesi içermiyorsa derleyicinin işleyiciyi kullanmasına izin verilir çünkü işleyici türünün ek karmaşık analiz olmadan güvenli bir şekilde kullanıldığını saptayabiliriz çünkü ilişkilendirilmiş dize ifadesi değerlendirildikten sonra işleyici bırakılır.
Aç Soru:
Bunun yerine derleyicinin DefaultInterpolatedStringHandler bilmesini sağlamak ve string.Format çağrısını tamamen atlamak mı istiyoruz? Bu, bir yöntemi direk olarak insanlara göstermek istemediğimizde, el ile string.Formatçağırırken gizlememize imkan tanır.
Yanıt: Evet.
Aç Soru:
Biz de System.IFormattable ve System.FormattableString için işleyiciler ister miyiz?
Yanıt: Hayır.
İşleyici desen kodgeni
Bu bölümde, yöntem çağırma çözümlemesi §12.8.10.2içinde listelenen adımları ifade eder.
Kurucu çözümleme
Verilen bir applicable_interpolated_string_handler_typeT ve bir interpolated_string_expressioniile, T üzerinde geçerli bir oluşturucu için yöntem çağırma çözümlemesi ve doğrulaması aşağıdaki gibi gerçekleştirilir:
- Örnek oluşturucular için üye araması
Tüzerinde gerçekleştirilir. Elde edilen yöntem grubuMolarak adlandırılır. - bağımsız değişken listesi
Aaşağıdaki gibi oluşturulur:- İlk iki bağımsız değişken, sırasıyla
i'nin gerçek uzunluğunu ve içindekiibileşenlerinin sayısını temsil eden tamsayı sabitleridir. -
ipiyöntemindeki bazı parametreM1bağımsız değişkeni olarak kullanılırsa ve parametrepiSystem.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttributeile ilişkilendirilirse, bu özniteliğinArgxdizisindeki her adArgumentsiçin derleyici bunu aynı ada sahip bir parametrepxile eşleştirir. Boş dize,M1alıcısı ile eşleştirilir.- herhangi bir
ArgxM1parametresiyle eşleştirilemiyorsa veyaArgxM1alıcısını isterse veM1statik bir yöntemse, bir hata oluşturulur ve başka bir adım atılmaz. - Aksi takdirde, çözümlenen her
pxtürü,Argumentsdizisi tarafından belirtilen sırayla bağımsız değişken listesine eklenir. Herpx,ref'de belirtildiği gibi aynıM1semantiğiyle geçirilir.
- herhangi bir
- Son bağımsız değişken, bir
boolparametresi olarak geçirilenout.
- İlk iki bağımsız değişken, sırasıyla
- Geleneksel yöntem çağırma çözümlemesi, yöntem grubu
Mve bağımsız değişken listesiAile gerçekleştirilir. Yöntem çağırmanın son doğrulaması amacıyla,Mbağlamı, türü aracılığıylaTmember_access'i olarak değerlendirilir.- En iyi tek bir oluşturucu
Fbulunduysa, aşırı yükleme çözümlemesinin sonucuFolur. - Geçerli bir oluşturucu bulunamadıysa, 3. adım yeniden denenir, ardından
bool'den sonAparametresi kaldırılır. Bu yeniden deneme de geçerli üye bulamazsa bir hata oluşur ve başka bir adım atılmaz. - Tek en iyi yöntem bulunamadığında, aşırı yükleme çözümlemesinin sonucu belirsiz olur, bir hata oluşturulur ve başka bir adım atılmaz.
- En iyi tek bir oluşturucu
-
F'da son doğrulama gerçekleştirilir.-
Aöğelerinden herhangi birii'den sonra sözcük temelli olarak gerçekleşirse bir hata oluşur ve başka bir adım atılmaz. - herhangi bir
AFalıcısını isterse veFbir üye başlatıcıbaşlatıcı_hedef olarak kullanılan bir indeksleyici ise, bir hata bildirilir ve başka hiçbir adım atılmaz.
-
Not: Buradaki çözüm kasıtlı olarak Argx öğeler için diğer bağımsız değişkenler olarak geçirilen gerçek ifadeleri kullanmaz. Yalnızca dönüştürme sonrası türleri dikkate alınır. Bu, bir lambda'nın M1 geçirildiğinde tek bir temsilci türüne bağlı olduğu ve Mgeçirildiğinde farklı bir temsilci türüne bağlı olduğu beklenmeyen durumlarda çift dönüştürme sorunlarımız olmamasını sağlar.
Not: İç içe üye başlatıcılarının değerlendirme sırası nedeniyle dizin oluşturucuların üye başlatıcı olarak kullanılması durumunda bir hata bildiriyoruz. Şu kod parçacığını göz önünde bulundurun:
var x1 = new C1 { C2 = { [GetString()] = { A = 2, B = 4 } } };
/* Lowering:
__c1 = new C1();
string argTemp = GetString();
__c1.C2[argTemp][1] = 2;
__c1.C2[argTemp][3] = 4;
Prints:
GetString
get_C2
get_C2
*/
string GetString()
{
Console.WriteLine("GetString");
return "";
}
class C1
{
private C2 c2 = new C2();
public C2 C2 { get { Console.WriteLine("get_C2"); return c2; } set { } }
}
class C2
{
public C3 this[string s]
{
get => new C3();
set { }
}
}
class C3
{
public int A
{
get => 0;
set { }
}
public int B
{
get => 0;
set { }
}
}
__c1.C2[] bağımsız değişkenleri, dizin oluşturucunun alıcısına önce değerlendirilir. Bu senaryo için uygun bir çözüm bulabilsek de (ya __c1.C2 için geçici bir ayar oluşturup her iki dizin oluşturucu çağrısında paylaşarak ya da yalnızca ilk dizin oluşturucu çağrısında kullanarak ve bağımsız değişkeni her iki çağrıda da paylaşarak), bunun alışılmadık bir durum olduğu için kafa karıştırıcı olacağını düşünüyoruz. Bu nedenle senaryoyu tamamen yasaklıyoruz.
Açık Soru :
Createyerine bir oluşturucu kullanırsak, deseni biraz daraltma pahasına çalışma zamanı kodgenini geliştiririz.
Yanıt: Şimdilik yapıcıları kısıtlayacağız. Senaryo ortaya çıkarsa ileride genel bir Create yöntemi eklemeyi tekrar değerlendirebiliriz.
Append... yöntemi aşırı yükleme çözümlemesi
bir applicable_interpolated_string_handler_typeT ve bir interpolated_string_expressioniverildiğinde, Append... üzerinde geçerli T yöntemleri kümesi için aşırı yükleme çözümlemesi aşağıdaki gibi gerçekleştirilir:
-
içinde herhangi bir
ibileşeni varsa:-
TüzerindeAppendLiteraladlı üyenin araması gerçekleştirilir. Elde edilen yöntem grubuMlolarak adlandırılır. - bağımsız değişken listesi
Al,stringtüründe bir değer parametresiyle oluşturulur. - Geleneksel yöntem çağırma çözümlemesi, yöntem grubu
Mlve bağımsız değişken listesiAlile gerçekleştirilir. Yöntem çağırma son doğrulama amacıyla,Mlbağlamı örneği aracılığıyla birTolarak değerlendirilir.- Tek bir en iyi yöntem
Fibulunursa ve hata üretilmezse, yöntem çağırma çözümlemesinin sonucuFiolur. - Aksi takdirde bir hata bildirilir.
- Tek bir en iyi yöntem
-
-
'ün her
ixibileşeni için:-
TüzerindeAppendFormattedadlı üyenin araması gerçekleştirilir. Elde edilen yöntem grubuMfolarak adlandırılır. - Bağımsız değişken listesi
Afoluşturulmuş durumda:- İlk parametre, değere göre geçirilen
expression'ınix'idir. -
ixdoğrudan bir constant_expression bileşeni içeriyorsa, adıalignmentbelirtilen bir tamsayı değeri parametresi eklenir. - Eğer
ixdoğrudan bir interpolation_formattarafından takip edilirse, adıformatolarak belirtilen bir dize değeri parametresi eklenir.
- İlk parametre, değere göre geçirilen
- Geleneksel yöntem çağırma çözümlemesi, yöntem grubu
Mfve bağımsız değişken listesiAfile gerçekleştirilir. Yöntem çağırma son doğrulama amacıyla,Mfbağlamı örneği aracılığıyla birTolarak değerlendirilir.- Tek bir en iyi yöntem
Fibulunursa, yöntem çağırma çözümlemesinin sonucuFiolur. - Aksi takdirde bir hata bildirilir.
- Tek bir en iyi yöntem
-
- Son olarak, 1. ve 2. adımlarda bulunan her
Fiiçin son doğrulama gerçekleştirilir:- Herhangi bir
Fi,bool'i değer olarak veyavoidolarak döndürmezse, bir hata bildirilir. - Eğer tüm
Fiaynı türü döndürmezse, bir hata bildirilir.
- Herhangi bir
Bu kuralların Append... çağrıları için uzantı yöntemlerine izin vermediğini unutmayın. Bunu seçerseniz etkinleştirmeyi düşünebiliriz, ancak bu, GetEnumerator bir uzantı yöntemi olmasını izin ettiğimiz ancak Current veya MoveNext()izin vermediğimiz numaralandırıcı düzenine benzer.
Bu kurallar ve Append... veya CallerLineNumber (dil tarafından desteklendiğinde) gibi şeylerle çalışır.
Bazı işleyiciler, ilişkilendirilmiş bileşenlerle temel dizenin parçası olan bileşenler arasındaki farkı anlamak isteyeceğinden, temel öğeler ve ilişkilendirme delikleri için ayrı aşırı yükleme arama kurallarımız vardır.
Açık Soru
Yapılandırılmış günlük kaydı gibi bazı senaryolar, ilişkilendirme öğeleri için ad sağlayabilmeyi ister. Örneğin, bugün günlük kaydı çağrısı Log("{name} bought {itemCount} items", name, items.Count);gibi görünebilir.
{} içindeki adlar, çıkışların tutarlı ve tekdüzen olduğundan emin olmak için kayıtçılar için önemli yapı bilgileri sağlar. Bazı durumlarda, bunun için bir interpolasyon deliğinin :format bileşeni yeniden kullanılabilir, ancak birçok kayıt tutucu, biçim tanımlayıcılarını zaten anlar ve bu bilgilere dayalı olarak çıktı biçimlendirmesi için mevcut bir davranış sergiler. Bu adlandırılmış tanımlayıcıları yerleştirmeyi etkinleştirmek için kullanabileceğimiz bir söz dizimi var mı?
Bazı durumlar, C# 10'da destek sağlanması halinde, CallerArgumentExpressionile durumu idare edebilir. Ancak yöntem/özellik çağıran durumlar için bu yeterli olmayabilir.
Yanıt:
Şablonlu dizelerle ilgili ortogonal dil özelliklerinde keşfedebileceğimiz bazı ilginç bölümler olsa da, burada belirli bir söz diziminin, tanımlama grubu kullanmak gibi çözümlerden daha fazla bir avantaj sağlayacağını düşünmüyoruz: $"{("StructuredCategory", myExpression)}".
Dönüştürmeyi gerçekleştirme
Çözümlenen geçerli oluşturucu T ve yöntemleri olan bir iFc ve Append...Fa verildiğinde, i için azaltma aşağıdaki gibi gerçekleştirilir:
-
Fc'den önce leksik olarak gerçekleşenibağımsız değişkeni, önce leksik sırayla değerlendirilir ve daha sonra geçici değişkenler olarak depolanır. Sözcük düzenini korumak için,idaha büyük bir ifadenineparçası olarak meydana geldiyse,e'ten önce oluşanibileşenleri de yine sözcük sırasına göre değerlendirilecektir. -
Fc, ilişkilendirilmiş dize değişmez değerlerinin uzunluğu, ilişkilendirme delik sayısı, daha önce değerlendirilen bağımsız değişkenler ve birboolout bağımsız değişkeni (Fcson parametre olarak çözümlendiyse) ile çağrılır. Sonuç,ibgeçici bir değerde depolanır.- Sabit bileşenlerin uzunluğu, herhangi bir open_brace_escape_sequence ifadesi tek bir
{ile ve herhangi bir close_brace_escape_sequence ifadesi tek bir}ile değiştirildikten sonra hesaplanır.
- Sabit bileşenlerin uzunluğu, herhangi bir open_brace_escape_sequence ifadesi tek bir
-
Fcbir çıkış bağımsız değişkeni olarakboolile sona erdiyse, bubooldeğeri üzerinde bir denetim oluşturulur. Doğruysa,Fa'daki yöntemler çağrılır. Aksi takdirde çağrılmaz. -
Faxiçindeki herFaiçin,Faxgeçerli değişmez değer bileşeni veyaibifadesiyle uygun şekilde çağrılır.Faxbirbooldöndürürse, sonuç mantıksal olarak ve önceki tümFaxçağrılarıyla gösterilir.- Eğer
Fax,AppendLiteralçağrısıysa, sabit bileşen, herhangi bir açma_ayraç_kaçış_dizisi yerine tek bir{ve herhangi bir kapama_ayraç_kaçış_dizisi yerine tek bir}koyarak kaldırılır.
- Eğer
- Dönüştürmenin sonucu
ib.
Fc ve e'e geçirilen bağımsız değişkenlerin aynı geçici değişken olduğuna dikkat edin. Fc'nin gerektirdiği bir forma dönüştürmek için bu geçici değişken üzerinde dönüştürmeler yapılabilir, ancak örneğin lambdalar Fc ile earasında farklı bir temsilci türüne bağlanamaz.
Açık Soru
Bu azaltma, yanlış dönen bir Append... çağrısından sonra ilişkilendirilmiş dizenin sonraki bölümlerinin değerlendirilmeyeceği anlamına gelir. Bu durum, özellikle format boşluğu yan etkiler yaratıyorsa, çok kafa karıştırıcı olabilir. Bunun yerine önce tüm biçim deliklerini değerlendirebilir, ardından sonuçlarla birlikte tekrar tekrar Append... çağırabilir ve false döndürürse durabiliriz. Bu, tüm ifadelerin beklenebilecek şekilde değerlendirilmesini sağlar, ancak gerektiği kadar az yöntem çağırırız. Kısmi değerlendirme bazı daha gelişmiş durumlar için tercih edilebilir olsa da, genel durum için sezgisel olmayabilir.
Tüm biçim deliklerini her zaman değerlendirmek istediğimiz bir diğer alternatif de API'nin Append... sürümünü kaldırmak ve yalnızca yinelenen Format çağrıları yapmaktır. İşleyici, yalnızca bağımsız değişkeni atlayıp bu sürüm için hemen dönmesi gerekip gerekmediğini izleyebilir.
Answer: Deliklerin koşullu değerlendirmesini yapacağız.
Açık Soru
Dispose'ın çağrıldığından emin olmak için atılabilir işleyici türlerini bertaraf etmeli miyiz ve çağrıları try/finally ile sarmalayarak mı yapmalıyız? Örneğin, bcl'deki ilişkilendirilmiş dize işleyicisinin içinde kiralanmış bir dizi olabilir ve ilişkilendirme deliklerinden biri değerlendirme sırasında bir özel durum oluşturursa, atılmamışsa kiralanan dizi sızdırılabilir.
Yanıt: Hayır. işleyiciler yerel ayarlara (MyHandler handler = $"{MyCode()};gibi) atanabilir ve bu tür işleyicilerin ömrü belirsizdir. Foreach numaralandırıcılarından farklı olarak, yaşam süresi açıktır ve numaralandırıcı için kullanıcı tanımlı yerel oluşturulmaz.
Boş değer atanabilir başvuru türleri üzerindeki etkisi
Uygulamanın karmaşıklığını en aza indirmek için, bir yönteme veya dizin oluşturucuya bağımsız değişken olarak kullanılan ilişkilendirilmiş dize işleyici yapıcıları üzerinde null atanabilirlik analizi gerçekleştirme konusunda birkaç sınırlamamız vardır. Özellikle, oluşturucudan özgün bağlamdaki parametrelerin veya bağımsız değişkenlerin özgün yuvalarına bilgi akışı sağlamayız ve oluşturucu parametre türlerini, içeren yöntemdeki tür parametreleri için genel tür çıkarımını belirlemek için kullanmayız. Bunun etki yaratabileceği bir örnek:
string s = "";
C c = new C();
c.M(s, $"", c.ToString(), s.ToString()); // No warnings on c.ToString() or s.ToString(), as the `MaybeNull` does not flow back.
public class C
{
public void M(string s1, [InterpolatedStringHandlerArgument("", "s1")] CustomHandler c1, string s2, string s3) { }
}
[InterpolatedStringHandler]
public partial struct CustomHandler
{
public CustomHandler(int literalLength, int formattedCount, [MaybeNull] C c, [MaybeNull] string s) : this()
{
}
}
string? s = null;
M(s, $""); // Infers `string` for `T` because of the `T?` parameter, not `string?`, as flow analysis does not consider the unannotated `T` parameter of the constructor
void M<T>(T? t, [InterpolatedStringHandlerArgument("s1")] CustomHandler<T> c) { }
[InterpolatedStringHandler]
public partial struct CustomHandler<T>
{
public CustomHandler(int literalLength, int formattedCount, T t) : this()
{
}
}
Dikkat edilmesi gereken diğer noktalar
string türlerinin işleyicilere dönüştürülebilmesine izin ver
Tür yazarlığını basitleştirmek amacıyla, string türündeki ifadelerin applicable_interpolated_string_handler_typesiçinde örtük olarak dönüştürülebilir olmasını düşünebiliriz. Bugün önerildiği gibi, yazarların muhtemelen hem bu işleyici türünde hem de normal string türlerinde aşırı yükleme yapmaları gerekecek, böylece kullanıcılarının farkı anlamasına gerek kalmayacak.
string bir ifade önceden doldurulmuş uzunluğu ve doldurulacak 0 delikli expression.Length ilişkilendirme olarak görüntülenebildiği için bu rahatsız edici ve belirgin olmayan bir ek yük olabilir.
Bu, yeni API'lerin stringdestekleyen bir aşırı yüklemeyi yapmadan yalnızca bir işleyiciyi kullanıma sunmasına olanak sağlar. Ancak, ifadeden daha iyi dönüştürmeye yönelik değişiklikler gereksinimini ortadan kaldırmaz, bu nedenle işe yarasa da gereksiz bir ek yük olabilir.
Yanıt:
Bunun kafa karıştırıcı olabileceğini düşünüyoruz ve özel işleyici türleri için kolay bir geçici çözüm vardır: dizeden kullanıcı tanımlı bir dönüştürme ekleyin.
Yığınsız dizeler için span'ları ekleme
ValueStringBuilder bugünkü haliyle 2 oluşturucuya sahiptir: biri bir sayıyı alır ve aceleci bir şekilde heap üzerinde ayırır, diğeri ise bir Span<char>alır. Bu Span<char> genellikle çalışma zamanı kod tabanında ortalama 250 öğe civarında sabit bir boyuta sahiptir. Bu türü gerçekten değiştirmek için, yalnızca sayı sürümünü değil, aynı zamanda GetInterpolatedStringalan Span<char> yöntemlerini de tanıdığımız bir uzantıyı göz önünde bulundurmamız gerekir. Ancak, burada çözülmesi gereken birkaç olası dikenli durum görüyoruz:
- Sık çalışan bir döngüde tekrar tekrar stackalloc kullanmak istemiyoruz. Bu özelliğin uzantısını yapacak olsaydık, stackalloc'd span'ı döngü yinelemeleri arasında paylaşmak isteyebiliriz.
Span<T>'nun yığına depolanamayan bir ref yapısı olduğunu ve kullanıcıların oSpan'e bir referans çıkarmak için oldukça kurnaz olmaları gerektiğini biliyoruz (örneğin, böyle bir işleyiciyi kabul eden bir yöntem oluşturup, ardındanSpan'yi işleyiciden kasıtlı olarak alıp çağırana geri döndürmek gibi). Ancak önceden ayırma işlemi başka sorulara neden olur:- Stackalloc'u hevesle mi kullanmalıyız? Döngüye hiçbir zaman girilmezse ya da alana ihtiyaç duymadan önce çıkarsa ne olur?
- Stackalloc işlemini hevesle kullanmıyorsak, bu her döngüde gizli bir dal tanıtacağımız anlamına mı gelir? Döngülerin çoğu büyük olasılıkla bunu önemsemez, ancak maliyeti ödemek istemeyen bazı sıkı döngüleri etkileyebilir.
- Bazı dizeler oldukça büyük olabilir ve
stackallociçin uygun miktar, çalışma zamanı faktörleri de dahil olmak üzere çeşitli faktörlere bağlıdır. C# derleyicisinin ve belirtiminin bunu önceden belirlemesini istemiyoruz, bu nedenle https://github.com/dotnet/runtime/issues/25423 çözmek ve derleyicinin bu durumlarda çağırması için bir API eklemek istiyoruz. Ayrıca önceki döngüdeki noktalara daha fazla artı ve eksi ekler; burada yığında birçok kez veya ihtiyaç duyulmadan önce büyük diziler ayırmak istemeyiz.
Yanıt:
Bu, C# 10'un kapsamı dışındadır. Daha genel params Span<T> özelliğine baktığımızda genel olarak buna bakabiliriz.
API'nin deneme dışı sürümü
Kolaylık olması için, bu taslak şu anda yalnızca bir Append... yönteminin belirlenmesini önerir ve her zaman başarılı olan yöntemler (InterpolatedStringHandlergibi) her zaman doğru döndürecektir.
Bu, kullanıcının bir hata oluşursa veya gereksizse - örneğin günlüğe kaydetme durumunda - biçimlendirmeyi durdurmak istediği kısmi biçimlendirme senaryolarını desteklemek için yapılmıştır, ancak standart iç içe geçmiş dize kullanımında çok sayıda gereksiz dala yol açabilir.
FormatX bir yöntem yoksa yalnızca Append... yöntemlerini kullandığımız bir ek eklemeyi düşünebiliriz, ancak hem Append... hem de FormatX çağrılarının bir karışımı varsa ne yapacağımızla ilgili soruları gündeme getirir.
Yanıt:
API'nin deneme olmayan sürümünü istiyoruz. Teklif bunu yansıtacak şekilde güncelleştirildi.
önceki bağımsız değişkenleri işleyiciye geçirme
Şu anda mevcut olan teklifte simetri eksikliği var: azaltılmış biçimde bir uzantı yöntemini çağırmak, uzantı yöntemini normal biçimde çağırmaktan farklı semantikler üretir. Bu, azaltılmış formun yalnızca şeker olduğu dildeki diğer konumlardan farklıdır. Bir yöntemi bağlarken tanıyacağımız ve işleyicideki oluşturucuya belirli parametrelerin geçirilmesi gerektiğini derleyiciye bildiren çerçeveye bir öznitelik eklemeyi öneriyoruz. Kullanım şöyle görünür:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public InterpolatedStringHandlerArgumentAttribute(string argument);
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments);
public string[] Arguments { get; }
}
}
Bunun kullanımı şu şekildedir:
namespace System
{
public sealed class String
{
public static string Format(IFormatProvider? provider, [InterpolatedStringHandlerArgument("provider")] ref DefaultInterpolatedStringHandler handler);
…
}
}
namespace System.Runtime.CompilerServices
{
public ref struct DefaultInterpolatedStringHandler
{
public DefaultInterpolatedStringHandler(int baseLength, int holeCount, IFormatProvider? provider); // additional factory
…
}
}
var formatted = string.Format(CultureInfo.InvariantCulture, $"{X} = {Y}");
// Is lowered to
var tmp1 = CultureInfo.InvariantCulture;
var handler = new DefaultInterpolatedStringHandler(3, 2, tmp1);
handler.AppendFormatted(X);
handler.AppendLiteral(" = ");
handler.AppendFormatted(Y);
var formatted = string.Format(tmp1, handler);
Yanıtlamamız gereken sorular:
- Bu deseni genel olarak beğendik mi?
- Bu argümanların işleyici parametresinden sonra gelmesine izin vermek istiyor muyuz? BCL'deki bazı mevcut desenler,
Utf8Formattergibi, formatlanacak değeri , formatta ihtiyaç duyulan şeyin önüne koyar. Bu desenlere en iyi şekilde uyum sağlamak için buna izin vermek isteyebiliriz, ancak bu sıra dışı sırada değerlendirme yapılmasının uygun olup olmadığını belirlememiz gerekiyor.
Yanıt:
Bunu desteklemek istiyoruz. Belirtim bunu yansıtacak şekilde güncelleştirildi. Bağımsız değişkenlerin çağrı noktasında sözcük düzeninde belirtilmesi gerekir ve başka bir deyişle, oluşturma yöntemi için gerekli bir bağımsız değişken, iç içe yerleştirilmiş dize ifadelerinden sonra belirtilirse bir hata verir.
await interpolasyon deliklerinde kullanımı
$"{await A()}" bugün geçerli bir ifade olduğundan, await ile ilişkilendirme deliklerini rasyonalize etmemiz gerekir. Bunu birkaç kuralla çözebiliriz:
-
string,IFormattableveyaFormattableStringolarak kullanılan ilişkilendirilmiş bir dizenin ilişkilendirme deliğinde birawaitvarsa, eski stil biçimlendiriciye geri dönün. - Interpolasyonlu bir dize, implicit_string_handler_conversion ve eğer applicable_interpolated_string_handler_type bir
ref structise,awaitbiçim deliklerinde kullanılmasına izin verilmez.
Temel olarak, bu kod sadeleştirmesi, eğer ref struct'ın yığına kaydedilmesi gerekmeyeceği garanti edilirse ve ilişkilendirme deliklerinde await'leri yasaklarsak, zaman uyumsuz bir yöntemde bir ref struct kullanılabilmesini sağlayabilir.
Alternatif olarak, ilişkilendirilmiş dizeler için çerçeve işleyicisi de dahil olmak üzere tüm işleyici türlerini ref olmayan yapılar haline getirmemiz yeterlidir. Ancak bu durum, bir gün herhangi bir geçici bellek alanı ayırması gerekmeyen bir Span sürümünü tanımamızı engeller.
Yanıt:
Entegre dize işleyicilerini diğer türler gibi ele alacağız: Bu, işleyici türü bir referans yapısıysa ve geçerli bağlam referans yapıların kullanımına izin vermiyorsa, işleyicinin burada kullanılması yasadışı demektir. Dize olarak kullanılan dize değişmez değerlerinin indirilmesiyle ilgili belirtim, derleyicinin uygun gördüğü kurallara karar vermesine izin vermek için kasıtlı olarak belirsizdir, ancak özel işleyici türleri için dilin geri kalanıyla aynı kuralları izlemeleri gerekir.
Ref parametreleri olarak işleyiciler
Bazı işleyiciler ref parametreleri olarak geçirilebilir (in veya ref). İkisine de izin vermeli miyiz? Öyleyse, ref işleyicisi nasıl görünür?
ref $"" karışıklığa neden olabilir çünkü aslında dizeyi referans ile geçirmiyorsunuz, referanstan oluşturulan işleyiciyi referansla geçiriyorsunuz ve bu, zaman uyumsuz yöntemlerle benzer olası sorunlar taşıyabilir.
Yanıt:
Bunu desteklemek istiyoruz. Belirtim bunu yansıtacak şekilde güncelleştirildi. Kurallar, değer türlerindeki uzantı yöntemlerine uygulanan kuralları yansıtmalıdır.
İkili ifadeler ve dönüştürmeler aracılığıyla ilişkilendirilmiş dizeler
Bu teklif, ilişkilendirilmiş dizelerin bağlamını hassas hale getirdiğinden, derleyicinin tamamen ilişkilendirilmiş dizelerden oluşan bir ikili ifadeyi veya türe tabi olan bir ilişkilendirilmiş dizeyi aşırı yükleme çözümlemesi amacıyla ilişkilendirilmiş dize değişmez değeri olarak işlemesine izin vermek istiyoruz. Örneğin, aşağıdaki senaryoyu inceleyin:
struct Handler1
{
public Handler1(int literalLength, int formattedCount, C c) => ...;
// AppendX... methods as necessary
}
struct Handler2
{
public Handler2(int literalLength, int formattedCount, C c) => ...;
// AppendX... methods as necessary
}
class C
{
void M(Handler1 handler) => ...;
void M(Handler2 handler) => ...;
}
c.M($"{X}"); // Ambiguous between the M overloads
Bu durum belirsizliğe neden olabilir ve çözümlemek için Handler1 veya Handler2'e dönüştürülmesi gerekebilir. Ancak, bu dönüştürmeyi yaparken, yöntem alıcısından gelen bağlam bilgisini kaybedebiliriz, çünkü cbilgilerini dolduracak hiçbir şey olmadığı için dönüştürme başarısız olur. Dizelerin ikili birleştirmesinde de benzer bir sorun ortaya çıkar: Kullanıcı satır kaydırmayı önlemek için değişmez değeri birkaç satır arasında biçimlendirmek isteyebilir, ancak artık işleyici türüne dönüştürülebilir ilişkilendirilmiş bir dize değişmez değeri olmayacağından bunu yapamayacaktır.
Bu durumları çözmek için aşağıdaki değişiklikleri yaparız:
- Tamamen interpolated_string_expressions’den oluşan ve yalnızca işleçleri kullanan bir
+, dönüştürme ve aşırı yük çözülmesi amacıyla bir interpolated_string_literal olarak kabul edilir. Son ilişkilendirilmiş dize, tüm tek tek interpolated_string_expression bileşenleri soldan sağa mantıksal olarak birleştirilerek oluşturulur. - İşleneni interpolated_string_expressions olan işlecine sahip bir
asveya relational_expression, dönüştürmeler ve aşırı yükleme çözümlemesi amacıyla interpolated_string_expressions olarak kabul edilir.
Açık Sorular:
Bunu yapmak istiyor musunuz? Örneğin, System.FormattableStringiçin bunu yapmayız, ancak bu farklı bir satıra ayrılabilir, ancak bu bağlama bağımlı olabilir ve bu nedenle farklı bir satıra bölünemez. ayrıca FormattableString ve IFormattableile ilgili aşırı yükleme çözümüyle ilgili bir sorun yoktur.
Yanıt:
Bunun, eklemeli ifadeler için geçerli bir kullanım örneği olduğunu düşünüyoruz, ancak yayın sürümünün şu anda yeterince ilgi çekici olmadığını düşünüyoruz. Gerekirse daha sonra ekleyebiliriz. Belirtim bu kararı yansıtacak şekilde güncelleştirildi.
Diğer kullanım örnekleri
Bu deseni kullanan önerilen işleyici API'lerinin örnekleri için bkz. https://github.com/dotnet/runtime/issues/50635.
C# feature specifications