Aracılığıyla paylaş


Koleksiyon ifadesi elemanından daha iyi dönüştürme

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çinbelirtimleri makalesinde bulabilirsiniz.

Şampiyon sorunu: https://github.com/dotnet/csharplang/issues/8374

Özet

paramsile daha tutarlı hale getirmek ve mevcut belirsizlik senaryolarını daha iyi ele almak için dönüştürme kurallarının güncellemeleri. Örneğin, ReadOnlySpan<string> vs ReadOnlySpan<object> şu anda [""]için aşırı yükleme çözümlemesi sırasında belirsizliklere neden olabilir.

Ayrıntılı Tasarım

İfade kurallarının daha iyi dönüşümleri aşağıdadır. Bunlar, https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#overload-resolutioniçindeki kuralların yerini alır.

Bu kurallar şunlardır:

bir ifadeden türüne dönüştüren örtük dönüştürme ve bir ifadeden türüne dönüştüren örtük dönüştürme göz önünde bulundurulduğunda, eğer aşağıdakilerden biri geçerliyse, bir de daha iyi bir dönüştürme:

  • ,bir koleksiyon ifadesidir ve , ifade daha iyi bir koleksiyon dönüştürme işlemidir
  • bir koleksiyon ifadesi değildir ve aşağıdakilerden biri geçerlidir:
    • E, T₁ ile tam olarak eşleşir ve E, T₂ ile tam olarak eşleşmez.
    • E, T₁ ve T₂'nin her ikisiyle de veya hiçbiriyle de tam olarak eşleşir ve T₁, 'den T₂
  • E bir yöntem grubudur, ...

ifadesinden daha iyi koleksiyon dönüştürmesi için olarak aşağıdaki gibi yeni bir tanım ekliyoruz:

Verilen:

  • E, [EL₁, EL₂, ..., ELₙ] öğe ifadeleri içeren bir koleksiyon ifadesidir.
  • T₁ ve T₂ koleksiyon türleridir
  • E₁ T₁ öğe türüdür
  • E₂ T₂ öğe türüdür
  • CE₁ᵢ ELᵢ'den E₁ dönüştürme dizisidir
  • CE₂ᵢ ELᵢ'den E₂ dönüştürme dizisidir

E₁'dan E₂'e birim dönüşümü varsa, öğe dönüşümleri de birbirleri kadar iyidir. Aksi takdirde, öğesine yapılan element dönüşümleri, aşağıdaki durumlarda öğe dönüşümlerinden 'ye daha iyi .

  • her ELᵢiçin CE₁ᵢ en az CE₂ᵢkadar iyidir ve
  • CE₁ᵢ CE₂ᵢ'den daha iyi olduğu en az bir i vardır. Aksi takdirde, öğe dönüştürmeleri kümesi diğerinden daha iyi değildir ve aynı zamanda birbirleri kadar iyi değildir.
    Dönüşüm karşılaştırmaları, ELᵢ bir spread öğesi değilse ifadeden daha iyi dönüşüm kullanılarak yapılır. ELᵢ bir yayılma elemanıysa, yayma koleksiyonunun eleman türünden E₁ veya E₂'ye daha iyi bir dönüşüm kullanırız.

, ifadeden'ün, 'e göre daha iyi bir koleksiyon dönüştürme işlemidir.

  • hem hem de span türleri değildir, ve örtük olarak dönüştürülebilir, ve için örtük olarak dönüştürülemez, veya
  • 'un 'e bir kimlik dönüşümü yoktur ve 'ye öğe dönüşümleri,'dan 'ye öğe dönüşümlerine göre daha iyidir veya
  • E₁ E₂kimlik dönüştürmesi vardır ve aşağıdakilerden biri geçerlidir:
    • T₁ System.ReadOnlySpan<E₁>eşittir, ve T₂System.Span<E₂>eşittir, veya
    • T₁ System.ReadOnlySpan<E₁> veya System.Span<E₁>'dir ve T₂, öğe türü olan bir E₂'dir.

Aksi takdirde, koleksiyon türü daha iyi değildir ve sonuç belirsizdir.

Not

Bu kurallar, farklı öğe türlerini içeren ve koleksiyon türleri arasında bir dönüşüm olmaksızın aşırı yüklemeler sunan yöntemlerin boş koleksiyon ifadeleri için belirsiz olduğunu ifade eder. Örnek olarak:

public void M(ReadOnlySpan<int> ros) { ... }
public void M(Span<int?> span) { ... }

M([]); // Ambiguous

Senaryo:

Düz İngilizce olarak, koleksiyon türlerinin kendileri aynı olmalıdır, veya açıkça daha iyi (örneğin, List<T> ve List<T> aynıdır, List<T>IEnumerable<T>'den kesin olarak daha iyidir ve List<T> ile HashSet<T> karşılaştırılamaz) ve daha iyi koleksiyon türü için öğe dönüştürmeleri de aynı veya daha iyi olmalıdır (yani, ReadOnlySpan<object>için Span<string> ile [""] arasında karar veremeyiz. kullanıcının bu kararı alması gerekir). Bunun diğer örnekleri şunlardır:

T₁ T₂ E C₁ Dönüşümler C₂ Dönüşümler CE₁ᵢ ile CE₂ᵢ karşılaştırması Sonuç
List<int> List<byte> [1, 2, 3] [Identity, Identity, Identity] [Implicit Constant, Implicit Constant, Implicit Constant] CE₁ᵢ daha iyidir List<int> seçilir
List<int> List<byte> [(int)1, (byte)2] [Identity, Implicit Numeric] Uygulanamaz T₂ uygulanamaz List<int> seçilir
List<int> List<byte> [1, (byte)2] [Identity, Implicit Numeric] [Implicit Constant, Identity] İkisi de daha iyi değil Belirsiz
List<int> List<byte> [(byte)1, (byte)2] [Implicit Numeric, Implicit Numeric] [Identity, Identity] CE₂ᵢ daha iyidir List<byte> seçilir
List<int?> List<long> [1, 2, 3] [Implicit Nullable, Implicit Nullable, Implicit Nullable] [Implicit Numeric, Implicit Numeric, Implicit Numeric] İkisi de daha iyi değil Belirsiz
List<int?> List<ulong> [1, 2, 3] [Implicit Nullable, Implicit Nullable, Implicit Nullable] [Implicit Numeric, Implicit Numeric, Implicit Numeric] CE₁ᵢ daha iyidir List<int?> seçilir
List<short> List<long> [1, 2, 3] [Implicit Numeric, Implicit Numeric, Implicit Numeric] [Implicit Numeric, Implicit Numeric, Implicit Numeric] CE₁ᵢ daha iyidir List<short> seçilir
IEnumerable<int> List<byte> [1, 2, 3] [Identity, Identity, Identity] [Implicit Constant, Implicit Constant, Implicit Constant] CE₁ᵢ daha iyidir IEnumerable<int> seçilir
IEnumerable<int> List<byte> [(byte)1, (byte)2] [Implicit Numeric, Implicit Numeric] [Identity, Identity] CE₂ᵢ daha iyidir List<byte> seçilir
int[] List<byte> [1, 2, 3] [Identity, Identity, Identity] [Implicit Constant, Implicit Constant, Implicit Constant] CE₁ᵢ daha iyidir int[] seçilir
ReadOnlySpan<string> ReadOnlySpan<object> ["", "", ""] [Identity, Identity, Identity] [Implicit Reference, Implicit Reference, Implicit Reference] CE₁ᵢ daha iyidir ReadOnlySpan<string> seçilir
ReadOnlySpan<string> ReadOnlySpan<object> ["", new object()] Uygulanamaz [Implicit Reference, Identity] T₁ uygulanamaz ReadOnlySpan<object> seçilir
ReadOnlySpan<object> Span<string> ["", ""] [Implicit Reference] [Identity] CE₂ᵢ daha iyidir Span<string> seçilir
ReadOnlySpan<object> Span<string> [new object()] [Identity] Uygulanamaz T₁ uygulanamaz ReadOnlySpan<object> seçilir
ReadOnlySpan<InterpolatedStringHandler> ReadOnlySpan<string> [$"{1}"] [Interpolated String Handler] [Identity] CE₁ᵢ daha iyidir ReadOnlySpan<InterpolatedStringHandler> seçilir
ReadOnlySpan<InterpolatedStringHandler> ReadOnlySpan<string> [$"{"blah"}"] [Interpolated String Handler] [Identity] - Ancak sabit CE₂ᵢ daha iyidir ReadOnlySpan<string> seçilir
ReadOnlySpan<string> ReadOnlySpan<FormattableString> [$"{1}"] [Identity] [Interpolated String] CE₂ᵢ daha iyidir ReadOnlySpan<string> seçilir
ReadOnlySpan<string> ReadOnlySpan<FormattableString> [$"{1}", (FormattableString)null] Uygulanamaz [Interpolated String, Identity] T₁ uygulanamaz ReadOnlySpan<FormattableString> seçilir
HashSet<short> Span<long> [1, 2] [Implicit Constant, Implicit Constant] [Implicit Numeric, Implicit Numeric] CE₁ᵢ daha iyidir HashSet<short> seçilir
HashSet<long> Span<short> [1, 2] [Implicit Numeric, Implicit Numeric] [Implicit Constant, Implicit Constant] CE₂ᵢ daha iyidir Span<short> seçilir

Açık sorular

ReadOnlySpan / Span diğer türlere göre ne kadar önceliklendirmemiz gerekir?

Bugün belirtildiği gibi, aşağıdaki aşırı yüklemeler belirsiz olacaktır:

C.M1(["Hello world"]); // Ambiguous, no tiebreak between ROS and List
C.M2(["Hello world"]); // Ambiguous, no tiebreak between Span and List

C.M3(["Hello world"]); // Ambiguous, no tiebreak between ROS and MyList.

C.M4(["Hello", "Hello"]); // Ambiguous, no tiebreak between ROS and HashSet. Created collections have different contents

class C
{
    public static void M1(ReadOnlySpan<string> ros) {}
    public static void M1(List<string> list) {}

    public static void M2(Span<string> ros) {}
    public static void M2(List<string> list) {}

    public static void M3(ReadOnlySpan<string> ros) {}
    public static void M3(MyList<string> list) {}

    public static void M4(ReadOnlySpan<string> ros) {}
    public static void M4(HashSet<string> hashset) {}
}

class MyList<T> : List<T> {}

Buraya ne kadar gitmek istiyoruz? List<T> değişkeni mantıklı görünüyor ve List<T>'in birçok alt türü mevcut. Ancak HashSet sürümünün çok farklı bir semantiği var, bu API'de gerçekten ReadOnlySpan'den daha kötü olduğundan ne kadar eminiz?