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.
Uyarı
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ında yakalanır.
Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek için
Şampiyonluk sorunu: https://github.com/dotnet/csharplang/issues/8714
Özet
Programlama dilinde Span<T> ve ReadOnlySpan<T> için yeni örtük dönüştürme türlerini içeren birinci sınıf destek sunarız ve bu integral türleriyle daha doğal programlamayı sağlamak için onları daha fazla yerde göz önünde bulundururuz.
Motivasyon
C# 7.2'ye girişlerinden bu yana, Span<T> ve ReadOnlySpan<T>, dil ve temel sınıf kitaplığına (BCL) birçok temel yolla entegre oldular. Bu, geliştiricilerin güvenliğine mal olmadan performansı geliştirdiği için geliştiriciler için harikadır. Ancak, dil bu türlere birkaç önemli şekilde mesafeli yaklaşarak API'lerin amacını ifade etmeyi zorlaştırır ve yeni API'ler için büyük ölçüde yüzey alanı yinelemesine yol açar. Örneğin, BCL .NET 9'da bir dizi yeni tensor ilkel API eklemiştir, ancak bu API'lerin tümü belirli bir platformda ReadOnlySpan<T>sunulmaktadır. C# , ReadOnlySpan<T>ve Span<T>arasındaki T[]ilişkiyi tanımaz, bu nedenle bu türler arasında kullanıcı tanımlı dönüştürmeler olsa bile, uzantı yöntemi alıcıları için kullanılamazlar, diğer kullanıcı tanımlı dönüştürmelerle oluşturamaz ve tüm genel tür çıkarım senaryolarında yardımcı olmazlar.
Kullanıcıların açık dönüştürmeleri veya tür bağımsız değişkenlerini kullanması gerekir; bu da IDE araçlarının kullanıcılara bu API'leri kullanma konusunda yol göstermediği anlamına gelir, çünkü IDE'ye dönüştürme işleminden sonra bu türleri geçirmenin geçerli olduğunu hiçbir şey göstermez. Bu API stili için maksimum kullanılabilirlik sağlamak amacıyla, BCL'nin hiçbir gerçek kazanç sağlamadan çok fazla gereksiz tekrar edilen kodu yönetmek zorunda kalacağı bir dizi Span<T> ve T[] aşırı yüklemeleri tanımlaması gerekecek. Bu teklif, dilin bu türleri ve dönüştürmeleri daha doğrudan tanımasını sağlayarak sorunu çözmeyi hedefler.
Örneğin, BCL aşağıdaki gibi herhangi bir MemoryExtensions yardımcı işlevin yalnızca bir aşırı yükleme versiyonunu ekleyebilir:
int[] arr = [1, 2, 3];
Console.WriteLine(
arr.StartsWith(1) // CS8773 in C# 13, permitted with this proposal
);
public static class MemoryExtensions
{
public static bool StartsWith<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T> => span.Length != 0 && EqualityComparer<T>.Default.Equals(span[0], value);
}
Daha önce uzantı alıcıları için kullanıcı tanımlı dönüştürmeler (Span/array/ReadOnlySpan arasında var olan) dikkate alınmadığından, uzantı yöntemini Span/dizi türündeki değişkenlerde kullanılabilir hale getirmek için Span ve dizi aşırı yüklemeleri gerekiyordu.
Ayrıntılı Tasarım
Bu teklifteki değişiklikler LangVersion >= 14'e bağlı olacaktır.
Yayılma alanı dönüştürmeleri
§10.2.1'deki listeye yeni bir örtük dönüşüm türü, bir örtük span dönüşümü, ekliyoruz. Bu dönüşüm, bir türden donüşümdür ve aşağıdaki gibi tanımlanır.
Örtük bir span dönüştürme, array_types, System.Span<T>, System.ReadOnlySpan<T> ve string değerlerinin aşağıdaki gibi birbiriyle dönüştürülmesini sağlar:
- Herhangi bir tek boyutlu
array_type'dan, öğe türüEiolanSystem.Span<Ei>'ya - Herhangi bir tek boyutlu
array_typeöğe türüEiolanSystem.ReadOnlySpan<Ui>,Ei'ün e kovaryans-dönüştürülebilir (Ui) olması şartıyla. -
System.Span<Ti>ileSystem.ReadOnlySpan<Ui>arasında,Ti'e kovaryans-dönüştürülebilir (Ui) olmak koşuluyla -
System.ReadOnlySpan<Ti>ileSystem.ReadOnlySpan<Ui>arasında,Ti'e kovaryans-dönüştürülebilir (Ui) olmak koşuluyla -
string'danSystem.ReadOnlySpan<char>
Span/ReadOnlySpan türlerinden herhangi biri, ref struct tipi olması ve tam adıyla eşleşmesi şartıyla dönüştürme için geçerli kabul edilir (LDM 2024-06-24).
Standart örtük dönüştürmeler (§10.4.2) listesine örtük span dönüştürmesi de ekliyoruz. Bağımsız değişken çözümlemesi gerçekleştirirken, daha önce bağlı API teklifinde olduğu gibi, aşırı yükleme çözümlemesinin bunları dikkate almasını sağlar.
Açıkça belirtilmiş aralık dönüştürmeleri şunlardır:
- Tüm örtük span dönüşümleri.
- Bir array_type türünden, belirli bir öğe türü ile
Ti'nin mevcut olduğunda,System.Span<Ui>'denSystem.ReadOnlySpan<Ui>'ye açık bir başvuru dönüştürmesi gerekliliği ile,TiveyaUitürüne.
Diğer standart açık dönüştürmelerden (§10.4.3) farklı olarak standart açık aralık dönüştürmesi yoktur ve bu dönüştürmeler her zaman ters standart örtük dönüştürme ile sağlanır.
Kullanıcı tanımlı dönüştürmeler
Örtük veya açık bir span dönüştürmesi olan türler arasında dönüştürme yapılırken kullanıcı tanımlı dönüştürmeler dikkate alınmaz.
Örtük span dönüştürmeleri, kullanıcı tanımlı olmayan bir dönüştürmenin bulunduğu türler arasında kullanıcı tanımlı işleç tanımlamanın mümkün olmadığı kuralından muaf tutulur (§10.5.2 İzin verilen kullanıcı tanımlı dönüştürmeler). Bu, BCL'nin C# 14'e geçtiğinde bile mevcut Span dönüştürme işleçlerini tanımlamaya devam edebilmesi için gereklidir (daha düşük LangVersion'lar için ve ayrıca bu işleçler yeni standart yayılma alanı dönüştürmelerinin codegen'inde kullanıldığından bunlar gereklidir). Ancak bir uygulama ayrıntısı olarak görüntülenebilir (codegen ve düşük LangVersion'lar belirtimin bir parçası değildir) ve Roslyn belirtimin bu bölümünü yine de ihlal eder (kullanıcı tanımlı dönüştürmelerle ilgili bu özel kural uygulanmaz).
Uzantı alıcısı
Ayrıca uzantı metodunun ilk parametresinin uygulanabilirliğini belirlerken (12.8.9.3), kabul edilebilir örtük dönüştürmeler listesine örtük span dönüştürmesi de ekledik (kalın olarak belirtilen değişiklik).
Uzantı yöntemi
Cᵢ.Mₑ, şu koşullarda uygun 'dur:
Cᵢgenel olmayan, iç içe olmayan bir sınıftırMₑ'ün adı tanımlayıcıdırMₑ, yukarıda gösterildiği gibi bağımsız değişkenlere statik bir yöntem olarak uygulandığında erişilebilir ve uygulanabilir- Örtük bir kimlik, referans
veya kutulama, kutulama veya span dönüşümü, expr ifadesinden ilk parametresinin türüneMₑmevcuttur. Bir yöntem grubu dönüştürmesi için aşırı yükleme çözümlemesi gerçekleştirildiğinde span dönüştürmesi dikkate alınmaz.
Yöntem grubu dönüştürmelerinde uzantı alıcısı için örtük span dönüştürmesinin dikkate alınmaması (LDM 2024-07-15), aşağıdaki kodun derleme zamanı hatası yerine çalışmaya devam etmesine neden olduğunu unutmayın: CS1113: Extension method 'E.M<int>(Span<int>, int)' defined on value type 'Span<int>' cannot be used to create delegates
using System;
using System.Collections.Generic;
Action<int> a = new int[0].M; // binds to M<int>(IEnumerable<int>, int)
static class E
{
public static void M<T>(this Span<T> s, T x) => Console.Write(1);
public static void M<T>(this IEnumerable<T> e, T x) => Console.Write(2);
}
Gelecekteki çalışmalarda mümkün olduğunca, yöntem grubu dönüştürmelerinde uzantı alıcısı için span dönüştürmesinin dikkate alınmadığını belirten bu koşulu kaldırmayı ve bunun yerine değişiklikleri uygulayarak yukarıdaki gibi senaryonun bunun yerine aşırı yüklemeyi başarıyla çağırmasını Span sağlayabiliriz:
- Derleyici, diziyi alıcı olarak alacak ve içinde span dönüştürmesi gerçekleştirecek bir thunk yayabilir (örneğin, temsilciyi el ile oluşturan kullanıcıya benzer şekilde
x => new int[0].M(x)). - Eğer uygulanırsa, değer temsilcileri
Span'yı doğrudan alıcı olarak kabul edebilir.
Fark
İmplicit span dönüşümündeki varyans bölümünün amacı, için bir miktar kovaryansı çoğaltmaktır. Burada genel değerler aracılığıyla varyansı tam olarak uygulamak için çalışma zamanı değişiklikleri gerekir (bkz. .. /csharp-13.0/ref-struct-interfaces.md for using ref struct types in generics), ancak önerilen .NET 9 API'sini kullanarak sınırlı miktarda kovaryans sağlayabiliriz: https://github.com/dotnet/runtime/issues/96952. Bu, dilin bazı senaryolarda System.ReadOnlySpan<T>'ı, T'in out T olarak bildirildiği gibi değerlendirmesine olanak sağlar. Bununla birlikte, bu varyant dönüşümünü tüm varyans senaryoları üzerinden derinlemesine incelemeyiz ve §18.2.3.3'teki varyans-dönüştürülebilir tanımına eklemeyiz. Gelecekte, buradaki varyansı daha derinden anlamak için çalışma zamanını değiştirirsek ve küçük bozulmaya neden olacak bu değişikliği dilde tamamen tanıyabilmek için adım atabiliriz.
Desen
ref structHerhangi bir desende tür olarak kullanıldığında yalnızca kimlik dönüştürmelerine izin verildiğini unutmayın:
class C<T> where T : allows ref struct
{
void M1(T t) { if (t is T x) { } } // ok (T is T)
void M2(R r) { if (r is R x) { } } // ok (R is R)
void M3(T t) { if (t is R x) { } } // error (T is R)
void M4(R r) { if (r is T x) { } } // error (R is T)
}
ref struct R { }
is-type işlecinin belirtiminden (§12.12.12.1):
[...] işleminin
E is Tsonucu,E'in null olmayıp referans dönüştürmesi, kutulama dönüştürmesi, kutudan çıkarma dönüştürmesi, sarmalama dönüştürmesi veya sarılmıştan çözme ileTtipine başarıyla dönüştürülüp dönüştürülemeyeceğini gösteren bir Boole değeridir.[...]
Eğer
Tnull atanamaz bir değer türüyse vetrueileDaynı türdeyse, sonuçTolur.
Bu özellik ile bu davranış değişmez, bu nedenle diziler için benzer desenler (varyans dahil) mümkün olsa da için Span/ReadOnlySpandesen yazmak mümkün olmayacaktır:
using System;
M1<object[]>(["0"]); // prints
M1<string[]>(["1"]); // prints
void M1<T>(T t)
{
if (t is object[] r) Console.WriteLine(r[0]); // ok
}
void M2<T>(T t) where T : allows ref struct
{
if (t is ReadOnlySpan<object> r) Console.WriteLine(r[0]); // error
}
Kod oluşturma
Bunları uygulamak için kullanılan çalışma zamanı yardımcılarının mevcut olup olmadığına bakılmaksızın dönüştürmeler her zaman mevcut olur (LDM 2024-05-13). Yardımcılar mevcut değilse, dönüştürme işlemini kullanmaya çalışmak, derleyicinin ihtiyaç duyduğu bir üyenin eksik olduğu bir derleme zamanı hatasına neden olur.
Derleyici, dönüştürmeleri uygulamak için aşağıdaki yardımcıları veya eşdeğerleri kullanmayı bekler:
| Dönüşüm | Yardımcıları |
|---|---|
| diziden Span'a |
static implicit operator Span<T>(T[]) (Span<T> içinde tanımlanır) |
| diziyi ReadOnlySpan'e |
static implicit operator ReadOnlySpan<T>(T[]) (ReadOnlySpan<T> içinde tanımlanır) |
| Span'dan ReadOnlySpan'a Dönüşüm |
static implicit operator ReadOnlySpan<T>(Span<T>) (Span<T> içinde tanımlanır) ve static ReadOnlySpan<T>.CastUp<TDerived>(ReadOnlySpan<TDerived>) |
| ReadOnlySpan'den ReadOnlySpan'e | static ReadOnlySpan<T>.CastUp<TDerived>(ReadOnlySpan<TDerived>) |
| string'den ReadOnlySpan'a | static ReadOnlySpan<char> MemoryExtensions.AsSpan(string) |
MemoryExtensions.AsSpan üzerinde tanımlanan eşdeğer örtük işleç yerine string kullanıldığına dikkat edin.
Bu, kodgenin LangVersions arasında farklı olduğu anlamına gelir (örtük işleç C# 13'te kullanılır; statik yöntem AsSpan C# 14'te kullanılır).
Öte yandan, dönüştürme .NET Framework'te uygulanabilir (AsSpan yöntemi orada bulunurken string işleç yoktur).
(ReadOnly)Span'a yapılan açık dizi dönüşümü, önce kaynak diziyi hedef öğe türüne sahip bir diziye açıkça dönüştürür, ardından örtük dönüşümlerde kullanılan yardımcı üzerinden (ReadOnly)Span'a dönüştürür; yani, karşılık gelen op_Implicit(T[]).
İfadedeki dönüşümü iyileştirme
İfade dönüşümünün iyileştirilmesi (§12.6.4.5), örtük span dönüştürmelerini tercih etmek için güncelleştirilir. Bu, koleksiyon ifadelerinin aşırı yükleme çözümleme değişikliklerini temel alır.
bir ifadeden türüne dönüştüren örtük
C₁dönüştürme ve bir ifadedenET₁türüneC₂dönüştüren örtükEdönüştürme, aşağıdakilerden birinin tutmasından dahaT₂iyi birC₁C₂
Ebir koleksiyon ifadesidir veC₁, ifadeden daha iyi bir koleksiyon dönüşümü sağlarC₂'e göre.Ebir koleksiyon ifadesi değildir ve aşağıdakilerden biri geçerlidir:
ET₁ile tam olarak eşleşir veEtam olarakT₂ile eşleşmez.E, neT₁ne deT₂ile tam olarak eşleşmez, veC₁örtülü bir aralık dönüşümüdür,C₂ise örtülü bir aralık dönüşümü değildirEtam olarak her ikisiyle de ya da hiçbiriyleT₁veT₂eşleşir, her ikisi de ya da hiçbiriC₁veC₂, örtük bir aralık dönüşümüdür, veT₁'den daha iyi birT₂Ebir yöntem grubudur,T₁dönüştürme için yöntem grubundan tek en iyi yöntemle uyumludur veC₁dönüştürmeT₂için yöntem grubundan tek en iyi yöntemle uyumlu değildirC₂
Daha iyi dönüşüm hedefi
Daha iyi dönüştürme hedefi (§12.6.4.7), ReadOnlySpan<T> yerine Span<T> tercih etmek için güncellenmiştir.
Verilen iki tür
T₁veT₂olduğunda, aşağıdaki koşullardan birine sahipse,T₁, 'den daha iyi bir dönüştürme hedefi olarakT₂kabul edilir.
T₁isSystem.ReadOnlySpan<E₁>,T₂isSystem.Span<E₂>, veE₁'tenE₂'ye bir kimlik dönüşümü mevcutturT₁System.ReadOnlySpan<E₁>,T₂System.ReadOnlySpan<E₂>veT₁'denT₂'e örtük bir dönüştürme var amaT₂'danT₁'ye örtük bir dönüştürme yok- En az biri
T₁veyaT₂değildir veSystem.ReadOnlySpan<Eᵢ>veSystem.Span<Eᵢ>değildir, ve 'denT₁'e örtük bir dönüştürme vardır ve 'denT₂'ye örtük dönüştürme yokturT₂T₁.- ...
Tasarım toplantıları:
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-04.md#preferring-readonlyspant-over-spant-conversions
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-09.md#first-class-span-open-questions
İyileştirme açıklamaları
İfade dönüşümünden daha iyi olma kuralı, yeni aralık dönüşümleri nedeniyle bir aşırı yüklenme uygulanabilir hale geldiğinde, yeni uygulanabilir olanın tercih edilmesi yoluyla başka bir aşırı yüklenmeyle olası belirsizliklerin önlenmesini sağlamalıdır.
Bu kural olmadan, C# 13'te başarıyla derlenen aşağıdaki kod, bir uzantı yöntemi alıcısı için geçerli olan yeni standart örtük diziden ReadOnlySpan'a yapılan yeni standart örtük dönüştürme nedeniyle C# 14'te belirsizlik hatasına neden olur:
using System;
using System.Collections.Generic;
var a = new int[] { 1, 2, 3 };
a.M();
static class E
{
public static void M(this IEnumerable<int> x) { }
public static void M(this ReadOnlySpan<int> x) { }
}
Kural ayrıca daha önce belirsizliklere neden olacak yeni API'lerin tanıtılmasına da izin verir, örneğin:
using System;
using System.Collections.Generic;
C.M(new int[] { 1, 2, 3 }); // would be ambiguous before
static class C
{
public static void M(IEnumerable<int> x) { }
public static void M(ReadOnlySpan<int> x) { } // can be added now
}
Uyarı
İyileştirme kuralı yalnızca LangVersion >= 14 içinde bulunan span dönüştürmeleri için tanımlandığından, API yazarları LangVersion <= 13 üzerindeki kullanıcıları desteklemeye devam etmek istediklerinde bu tür yeni overloadlar ekleyemezler.
Örneğin, .NET 9 BCL bu tür aşırı yüklemeler eklerse, TFM'ye net9.0 yükselten ancak daha düşük LangVersion'da kalan kullanıcılar mevcut kod için belirsizlik hataları alır.
Ayrıca aşağıda açık bir soruya da bakın.
Tür çıkarımı
Belirtimin tür çıkarımları bölümünü aşağıdaki gibi güncelliyoruz (kalın olarak gösterilen değişikliklerle).
12.6.3.9 Tam çıkarımlar
tam çıkarım bir tür
U'ten bir türV'ye şu şekilde yapılır:
V, sabitlenmemişXᵢelemanlardan biri ise,UXᵢiçin tam sınırlar kümesine eklenir.- Aksi takdirde,
V₁...VₑveU₁...Uₑkümeleri, aşağıdaki durumlardan birinin geçerli olup olmadığını denetleyerek belirlenir:
Vbir dizi türüV₁[...]veUaynı dereceye sahip bir dizi türüU₁[...]VbirSpan<V₁>veUbir dizi türüU₁[]veya birSpan<U₁>VbirReadOnlySpan<V₁>veUbir dizi türüU₁[]veyaSpan<U₁>veyaReadOnlySpan<U₁>V,V₁?türü olup,UiseU₁türüdür.V, bir yapılandırılmış türdürC<V₁...Vₑ>veU, bir yapılandırılmış türdürC<U₁...Uₑ>
Bu durumlardan herhangi biri geçerliyse, her bir 'den ilgiliUᵢ'e kadarVᵢyapılır.- Aksi takdirde çıkarım yapılmaz.
12.6.3.10 Alt sınır çıkarımları
Bir tür
'den türe olmak üzere türiçin alt sınır çıkarımı aşağıdaki gibi yapılır:
- Eğer
V, sabitlenmemişXᵢ’den biri ise,U,Xᵢiçin alt sınırlar kümesine eklenir.- Aksi takdirde,
VtürüV₁?veUtürüU₁?ise,U₁'denV₁'e daha düşük bir sınır çıkarımı yapılır.- Aksi takdirde,
U₁...UₑveV₁...Vₑkümeleri, aşağıdaki durumlardan birinin geçerli olup olmadığını denetleyerek belirlenir:
Vbir dizi türüdürV₁[...]veUaynı dereceye sahip bir dizi türüdürU₁[...]VbirSpan<V₁>veUbir dizi türüU₁[]veya birSpan<U₁>VbirReadOnlySpan<V₁>veUbir dizi türüU₁[]veyaSpan<U₁>veyaReadOnlySpan<U₁>VIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>veyaIList<V₁>biridir veUtek boyutlu bir dizi türüdürU₁[]V,class,struct,interfaceveyadelegatetürü oluşturulmuş birC<V₁...Vₑ>'tir veC<U₁...Uₑ>,U(veya,Ubirparametertürüyse, etkin temel sınıfı veya etkin arabirim kümesinin herhangi bir üyesi) ile aynı olan, doğrudan veya dolaylı olarakinherits'dan türemiş veyaC<U₁...Uₑ>'i uygulayan (doğrudan veya dolaylı olarak) benzersiz bir türdür.- ("Benzersizlik" kısıtlaması,
C<T>{} class U: C<X>, C<Y>{}UveyaC<T>olabileceğinden,U₁'denX'a çıkarılırkenYarabirimde çıkarım yapılmadığı anlamına gelir.)
Bu durumlardan herhangi biri geçerliyse, her birUᵢ'dan ilgiliVᵢ'e aşağıdaki gibi bir çıkarım yapılır:Uᵢbir başvuru türü olarak bilinmiyorsa tam bir çıkarım yapılır- Aksi takdirde,
Ubir dizi türündeysebir alt sınır çıkarımı yapılır;çıkarım,Vtürüne bağlıdır.
- Eğer
VbirSpan<Vᵢ>ise, tam bir çıkarım yapılır- Eğer
Vbir dizi türü veyaReadOnlySpan<Vᵢ>ise, bir alt sınır çıkarımı yapılır- Aksi takdirde, eğer
UbirSpan<Uᵢ>ise çıkarımVtürüne bağlıdır:
- Eğer
VbirSpan<Vᵢ>ise, tam bir çıkarım yapılır- Eğer
VbirReadOnlySpan<Vᵢ>ise, alt sınır çıkarımı yapılır- Aksi takdirde, eğer
UbirReadOnlySpan<Uᵢ>veVbirReadOnlySpan<Vᵢ>ise, bir alt sınır çıkarımı yapılır:- Aksi takdirde, eğer
VC<V₁...Vₑ>ise, çıkarımi-th'ünCtürü parametresine bağlıdır:
- Kovaryant ise bir alt sınır çıkarımı yapılır.
- Kontravar ise, üst sınır çıkarımı yapılır.
- Eğer sabitse, tam bir çıkarım gerçekleştirilir.
- Aksi takdirde çıkarım yapılmaz.
Üst sınır çıkarımı için hiçbir kural yoktur, çünkü bunlara isabet etmek mümkün olmaz.
Tür çıkarımı hiçbir zaman üst sınır olarak başlamaz, alt sınır çıkarımından ve değişken karşıtı tür parametresinden geçmesi gerekir.
"Kaynak tür bağımsız değişkeni, 'eğer Uᵢ bir başvuru türü olarak bilinmiyorsa kesin çıkarım yapılır' kuralı nedeniyle Span/ReadOnlySpan olamaz (bu türler başvuru türü olamaz)."
Ancak, üst sınır span çıkarımı yalnızca kaynak türü bir Span/ReadOnlySpanise geçerli olur çünkü aşağıdaki gibi kuralları olacaktır:
UbirSpan<U₁>veVbir dizi türüV₁[]veya birSpan<V₁>UbirReadOnlySpan<U₁>veVbir dizi türüV₁[]veyaSpan<V₁>veyaReadOnlySpan<V₁>
Kritik değişiklikler
Mevcut senaryoların dönüşümlerini değiştiren herhangi bir teklif gibi, bu teklif de bazı yeni hataya yol açan değişiklikler içerir. İşte birkaç örnek:
Bir dizide Reverse çağrısını yapma
x.Reverse() çağrısı, x türünün bir örneği olduğunda daha önce T[]e bağlanırken, şimdi IEnumerable<T> Enumerable.Reverse<T>(this IEnumerable<T>)e bağlanır.
Ne yazık ki bu API'ler uyumsuzdur (ikincisi ters çevirme işlemini yerinde yapar ve sonucu döndürür void).
.NET 10, diziye özgü bir aşırı yükleme IEnumerable<T> Reverse<T>(this T[])ekleyerek bunu azaltır, bkz https://github.com/dotnet/runtime/issues/107723. .
void M(int[] a)
{
foreach (var x in a.Reverse()) { } // fine previously, an error now (`Reverse` returns `void`)
foreach (var x in Enumerable.Reverse(a)) { } // workaround
}
Ayrıca bkz:
- https://developercommunity.visualstudio.com/t/Extension-method-SystemLinqEnumerable/10790323
- https://developercommunity.visualstudio.com/t/Compilation-Error-When-Calling-Reverse/10818048
- https://developercommunity.visualstudio.com/t/Version-17131-has-an-obvious-defect-th/10858254
- https://developercommunity.visualstudio.com/t/Visual-Studio-2022-update-breaks-build-w/10856758
- https://github.com/dotnet/runtime/issues/111532
- https://developercommunity.visualstudio.com/t/Backward-compatibility-issue-:-IEnumerab/10896189#T-ND10896782
Tasarım toplantısı: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#reverse
Belirsizlikler
Aşağıdaki örnekler daha önce Span aşırı yüklemesi için tür çıkarımında başarısız oldu, ancak şimdi diziden Span'a tür çıkarımı başarılı olur, bu nedenle bunlar belirsizdir.
Bu sorunu geçici olarak çözmek için kullanıcılar .AsSpan() kullanabilir veya API yazarları OverloadResolutionPriorityAttribute kullanabilir.
var x = new long[] { 1 };
Assert.Equal([2], x); // previously Assert.Equal<T>(T[], T[]), now ambiguous with Assert.Equal<T>(ReadOnlySpan<T>, Span<T>)
Assert.Equal([2], x.AsSpan()); // workaround
var x = new int[] { 1, 2 };
var s = new ArraySegment<int>(x, 1, 1);
Assert.Equal(x, s); // previously Assert.Equal<T>(T, T), now ambiguous with Assert.Equal<T>(Span<T>, Span<T>)
Assert.Equal(x.AsSpan(), s); // workaround
xUnit bunu azaltmak için daha fazla aşırı yükleme ekliyor: https://github.com/xunit/xunit/discussions/3021.
Tasarım toplantısı: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#new-ambiguities
Kovaryant diziler
Eş değişken diziler üzerinde çalışan aşırı yüklemeler IEnumerable<T> iken, şimdi tercih ettiğimiz Span<T> aşırı yüklemeleri çalışmıyor, çünkü dilim dönüştürme eş değişken diziler için bir ArrayTypeMismatchException fırlatıyor.
Muhtemelen aşırı Span<T> yükleme mevcut olmamalıdır, bunun yerine almalıdır ReadOnlySpan<T> .
Bu sorunu geçici olarak çözmek için kullanıcılar .AsEnumerable() kullanabilirler veya API yazarları, OverloadResolutionPriorityAttribute kullanabilir ya da daha iyi olma kuralı nedeniyle tercih edilen ReadOnlySpan<T> aşırı yüklemesini ekleyebilirler.
string[] s = new[] { "a" };
object[] o = s;
C.R(o); // wrote 1 previously, now crashes in Span<T> constructor with ArrayTypeMismatchException
C.R(o.AsEnumerable()); // workaround
static class C
{
public static void R<T>(IEnumerable<T> e) => Console.Write(1);
public static void R<T>(Span<T> s) => Console.Write(2);
// another workaround:
public static void R<T>(ReadOnlySpan<T> s) => Console.Write(3);
}
Tasarım toplantısı: https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-11.md#covariant-arrays
ReadOnlySpan'i Span yerine tercih etme
Üstünlük kuralı, covariant ArrayTypeMismatchException sorunları önlemek için Span aşırı yüklemeleri yerine ReadOnlySpan aşırı yüklemelerinin tercih edilmesine neden olur.
Bu, bazı senaryolarda, örneğin aşırı yüklemeler dönüş türüne göre farklılık gösterdiğinde derleme hatalarına yol açabilir.
double[] x = new double[0];
Span<ulong> y = MemoryMarshal.Cast<double, ulong>(x); // previously worked, now a compilation error (returns ReadOnlySpan, not Span)
Span<ulong> z = MemoryMarshal.Cast<double, ulong>(x.AsSpan()); // workaround
static class MemoryMarshal
{
public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span) => default;
public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span) => default;
}
Bkz. https://github.com/dotnet/roslyn/issues/76443.
İfade ağaçları
MemoryExtensions.Contains gibi diziler alan aşırı yüklemeler, ifade ağaçları içinde bile Enumerable.Contains gibi klasik aşırı yüklemelere göre tercih edilir, ancak ref yapıları yorumlayıcı motor tarafından desteklenmez.
Expression<Func<int[], int, bool>> exp = (array, num) => array.Contains(num);
exp.Compile(preferInterpretation: true); // fails at runtime in C# 14
Expression<Func<int[], int, bool>> exp2 = (array, num) => Enumerable.Contains(array, num); // workaround
exp2.Compile(preferInterpretation: true); // ok
Benzer şekilde, LINQ-to-SQL gibi çeviri motorlarının, ağaç ziyaretçileri Enumerable.Contains bekliyorlarsa, bunun yerine MemoryExtensions.Contains ile karşılaşacakları için bu duruma tepki vermeleri gerekir.
Ayrıca bkz:
- https://github.com/dotnet/runtime/issues/109757
- https://github.com/dotnet/docs/issues/43952
- https://github.com/dotnet/efcore/issues/35100
- https://github.com/dotnet/csharplang/discussions/8959
Tasarım toplantıları:
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-12-04.md#conversions-in-expression-trees
- https://github.com/dotnet/csharplang/blob/main/meetings/2025/LDM-2025-01-06.md#ignoring-ref-structs-in-expressions
Devralma yoluyla kullanıcı tanımlı dönüştürmeler
Standart örtük dönüştürmeler listesine örtük span dönüştürmeleri ekleyerek, kullanıcı tanımlı dönüştürmeler bir tür hiyerarşisine dahil olduğunda davranışı değiştirebiliriz. Bu örnekte, zaten yeni C# 14 davranışında olduğu gibi davranan bir tamsayı senaryosuyla karşılaştırıldığında değişikliğin gösterildiği gösterilmiştir.
Span<string> span = [];
var d = new Derived();
d.M(span); // Base today, Derived tomorrow
int i = 1;
d.M(i); // Derived today, demonstrates new behavior
class Base
{
public void M(Span<string> s)
{
Console.WriteLine("Base");
}
public void M(int i)
{
Console.WriteLine("Base");
}
}
class Derived : Base
{
public static implicit operator Derived(ReadOnlySpan<string> r) => new Derived();
public static implicit operator Derived(long l) => new Derived();
public void M(Derived s)
{
Console.WriteLine("Derived");
}
}
Ayrıca bkz. https://github.com/dotnet/roslyn/issues/78314
Uzantı yöntemi arama
Uzantı yöntemi aramasında örtük span dönüştürmelerine izin vererek, aşırı yükleme çözümlemesi ile çözümlenen uzantı yöntemini değiştirebiliriz.
namespace N1
{
using N2;
public class C
{
public static void M()
{
Span<string> span = new string[0];
span.Test(); // Prints N2 today, N1 tomorrow
}
}
public static class N1Ext
{
public static void Test(this ReadOnlySpan<string> span)
{
Console.WriteLine("N1");
}
}
}
namespace N2
{
public static class N2Ext
{
public static void Test(this Span<string> span)
{
Console.WriteLine("N2");
}
}
}
Açık sorular
Kısıtlanmamış iyilik kuralı
LangVersion'da betterness kuralını koşulsuz hale getirmeli miyiz? Bu, API yazarlarının eski LangVersion'larda veya diğer derleyicilerde veya dillerde (vb gibi) kullanıcıları bölmeden IEnumerable eşdeğerlerinin bulunduğu yeni Span API'leri eklemesine olanak sağlar. Ancak bu, kullanıcıların araç takımını güncelleştirdikten sonra farklı davranışlar elde edebileceği anlamına gelir (LangVersion veya TargetFramework'i değiştirmeden):
- Derleyici farklı aşırı yüklemeler seçebilir (teknik olarak hataya neden olan bir değişikliktir, ancak bu aşırı yüklemelerin eşdeğer davranışa sahip olacağını umuyoruz).
- Şu anda bilinmeyen başka kesintiler de ortaya çıkabilir.
OverloadResolutionPriorityAttribute daha eski LangVersions'ta da göz ardı edildiğinden, bu sorunu tamamen çözemez olduğunu unutmayın.
Ancak, özniteliğin tanınması gereken VB belirsizliklerini önlemek için bunu kullanmak mümkün olmalıdır.
Daha fazla kullanıcı tanımlı dönüşümü yoksayma durumu
Dil tarafından tanımlanan örtük ve açık span dönüştürmeleri olan bir tür çiftleri kümesini tanımladık.
Dil tarafından tanımlanan bir span dönüştürmesi T1'dan T2'e yapıldığında, T1'den T2'e olan herhangi bir kullanıcı tanımlı dönüştürme yoksayılır (span ve kullanıcı tanımlı dönüştürmenin örtük veya açık olmasına bakılmaksızın).
Bunun tüm koşulları içerdiğine dikkat edin; bu nedenle, örneğin Span<object> ile ReadOnlySpan<string> arasında bir span dönüştürmesi yoktur (bir span dönüştürmesi Span<T> ile ReadOnlySpan<U> arasında vardır, ancak T : U geçerli olmalıdır), dolayısıyla, varsa bu türler arasında kullanıcı tanımlı bir dönüşüm yapılabilir (çünkü dönüştürme operatörleri genel parametrelere sahip olamaz, bu yüzden Span<T> ile ReadOnlySpan<string> gibi özel bir dönüşüm olması gerekir).
Diğer dizi/Span/ReadOnlySpan/dize türlerinin bileşimleri arasında karşılık gelen dil tanımlı span dönüşümü olmadığında, bu kullanıcı tanımlı dönüştürmeleri de yoksaymalı mıyız?
Örneğin, ReadOnlySpan<T> öğesinden Span<T> öğesine kullanıcı tanımlı bir dönüştürme varsa, bunu yoksaymalı mıyız?
Dikkate alınması gereken olasılıklar:
-
T1öğesindenT2öğesine bir aralık dönüşümü mevcut olduğunda,T1öğesindenT2öğesine veyaT2öğesindenT1öğesine kullanıcı tanımlı herhangi bir dönüşümü yoksay. -
Arasında dönüştürme yapılırken kullanıcı tanımlı dönüştürmeler dikkate alınmaz
- herhangi bir tek boyutlu
array_typeveSystem.Span<T>/System.ReadOnlySpan<T>, - herhangi bir
System.Span<T>/System.ReadOnlySpan<T>bileşimi -
stringveSystem.ReadOnlySpan<char>.
- herhangi bir tek boyutlu
- Yukarıdaki gibi ancak son madde işareti noktasını şu şekilde değiştirerek:
-
stringveSystem.Span<char>/System.ReadOnlySpan<char>.
-
- Yukarıdaki gibi ancak son madde işareti noktasını şu şekilde değiştirerek:
-
stringveSystem.Span<T>/System.ReadOnlySpan<T>.
-
Teknik olarak belirtim, bu kullanıcı tanımlı dönüştürmelerin bazılarının bile tanımlanmasına izin vermez: Kullanıcı tanımlı olmayan bir dönüştürmenin var olduğu türler arasında kullanıcı tanımlı bir işleç tanımlamak mümkün değildir (§10.5.2).
Ancak Roslyn, belirtimin bu kısmını kasıtlı olarak ihlal ediyor. ve gibi Spanstring bazı dönüştürmelere yine de izin verilir (bu türler arasında dil tanımlı dönüştürme yoktur).
Bununla birlikte, sadece dönüştürmeleri görmezden gelmek yerine, bunların tanımlanmalarına izin vermemeyi seçebiliriz ve belki de en azından bu yeni span dönüştürmeleri için spesifikasyon ihlalini önleyebiliriz. Örneğin, bu dönüştürmeler tanımlanmışsa Roslyn'i gerçekten derleme zamanı hatası raporlayacak şekilde değiştirebiliriz (büyük olasılıkla BCL tarafından önceden tanımlanmış olanlar hariç).
Alternatifler
Her şeyi olduğu gibi tut.
C# feature specifications