Aracılığıyla paylaş


Kullanıcı tanımlı işleçler denetlendi

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ında yakalanır.

Ö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/4665

Özet

C#, kullanıcıların taşma davranışını ihtiyaçlarına göre etkinleştirmeleri veya devre dışı bırakmaları için aşağıdaki kullanıcı tanımlı işleçlerin checked varyantlarını tanımlamayı desteklemelidir.

Motivasyon

Kullanıcının türü bildirmesinin ve bir işlecin hem işaretli hem de işaretsiz sürümlerini desteklemesinin hiçbir yolu yoktur. Bu, kütüphane ekibi tarafından açıklanan önerilen generic math arabirimlerini kullanmak için çeşitli algoritmaları taşımayı zor hale getirir. Benzer şekilde, hataya neden olan değişiklikleri önlemek için dil aynı anda kendi desteğini göndermeden Int128 veya UInt128 gibi bir türü kullanıma sunmanız imkansız hale gelir.

Ayrıntılı tasarım

Sözdizimi

Operatörlerde dil bilgisi (§15.10), operatör belirtecinin hemen öncesinde checked anahtar sözcüğünden sonra operator anahtar sözcüğüne izin verilecek şekilde ayarlanacak.

overloadable_unary_operator
    : '+' | 'checked'? '-' | '!' | '~' | 'checked'? '++' | 'checked'? '--' | 'true' | 'false'
    ;

overloadable_binary_operator
    : 'checked'? '+'   | 'checked'? '-'   | 'checked'? '*'   | 'checked'? '/'   | '%'   | '&'   | '|'   | '^'   | '<<'
    | right_shift | '=='  | '!='  | '>'   | '<'   | '>='  | '<='
    ;
    
conversion_operator_declarator
    : 'implicit' 'operator' type '(' type identifier ')'
    | 'explicit' 'operator' 'checked'? type '(' type identifier ')'
    ;    

Mesela:

public static T operator checked ++(T x) {...}
public static T operator checked --(T x) {...}
public static T operator checked -(T x) {...}
public static T operator checked +(T lhs, T rhs) {...}
public static T operator checked -(T lhs, T rhs) {...}
public static T operator checked *(T lhs, T rhs) {...}
public static T operator checked /(T lhs, T rhs) {...}
public static explicit operator checked U(T x) {...}
public static T I1.operator checked ++(T x) {...}
public static T I1.operator checked --(T x) {...}
public static T I1.operator checked -(T x) {...}
public static T I1.operator checked +(T lhs, T rhs) {...}
public static T I1.operator checked -(T lhs, T rhs) {...}
public static T I1.operator checked *(T lhs, T rhs) {...}
public static T I1.operator checked /(T lhs, T rhs) {...}
public static explicit I1.operator checked U(T x) {...}

Aşağıda kısa olması için, checked anahtar sözcüğüne sahip bir işleç checked operator ve içermeyen bir işleç regular operatorolarak adlandırılır. Bu koşullar, checked formu olmayan işleçler için geçerli değildir.

Anlambilim

Bir işlemin sonucu hedef türde temsil edilemeyecek kadar büyük olduğunda, kullanıcı tanımlı bir checked operator özel durum oluşturması beklenir. Çok büyük olmanın ne anlama geldiği, aslında hedef türün doğasına bağlıdır ve dil tarafından belirlenmemiştir. Genellikle oluşan özel durum bir System.OverflowExceptionolur, ancak dilin bununla ilgili belirli gereksinimleri yoktur.

Kullanıcı tanımlı bir regular operator'nin, bir işlemin sonucu hedef türe sığmayacak kadar büyük olduğunda bir istisna oluşturmaması beklenir. Bunun yerine, kısaltılmış sonucu temsil eden bir örneğin döndürülmesi beklenir. Çok büyük olmak ve kesilmek ne anlama gelir sorusu, aslında hedef türün doğasına bağlıdır ve bu durum dil tarafından tanımlanmamıştır.

checked formu desteklenecek olan tüm mevcut kullanıcı tanımlı işleçler regular operatorskategorisine girer. Birçoğunun yukarıda belirtilen semantiği izlemeyeceği, ancak anlam analizi amacıyla derleyicinin bunların olduğunu varsayacağı anlaşılmaktadır.

bir checked operator içinde kontrollü ve kontrolsüz bağlam karşılaştırması

Bir checked operator gövdesindeki işaretli veya işaretsiz bağlam, checked anahtar sözcüğünün varlığından etkilenmez. Başka bir deyişle bağlam, işleç bildiriminin hemen başındaki bağlamla aynıdır. Algoritmanın bir bölümü varsayılan bağlamı kullanamıyorsa geliştiricinin bağlamı açıkça değiştirmesi gerekir.

Meta verilerdeki adlar

ECMA-335'in "I.10.3.1 Birli işleçler" bölümü, denetlenen , ve tekli işleçleri uygulayan yöntemlerin adları olarak ++, --, - içerecek şekilde ayarlanır.

ECMA-335'in "I.10.3.2 İkili işleçler" bölümü, denetlenen , , ve ikili işleçleri uygulayan yöntemlerin adları olarak +, -, *, / içerecek şekilde ayarlanır.

ECMA-335'in "I.10.3.3 Dönüştürme işleçleri" bölümü, denetlenen açık dönüştürme işlecini uygulayan bir yöntemin adı olarak op_CheckedExplicit içerecek şekilde ayarlanır.

Tekli operatörler

Unary checked operators§15.10.2kurallarına uyar.

Ayrıca, bir checked operator bildirimi, bir regular operator'in çift olarak bildirilmesini gerektirir (dönüş tipi de eşleşmelidir). Aksi takdirde derleme zamanı hatası oluşur.

public struct Int128
{
    // This is fine, both a checked and regular operator are defined
    public static Int128 operator checked -(Int128 lhs);
    public static Int128 operator -(Int128 lhs);

    // This is fine, only a regular operator is defined
    public static Int128 operator --(Int128 lhs);

    // This should error, a regular operator must also be defined
    public static Int128 operator checked ++(Int128 lhs);
}

İkili işleçler

İkili checked operators, §15.10.3kurallarına uyar.

Ayrıca, bir checked operator bildirimi, bir regular operator'in çift olarak bildirilmesini gerektirir (dönüş tipi de eşleşmelidir). Aksi takdirde derleme zamanı hatası oluşur.

public struct Int128
{
    // This is fine, both a checked and regular operator are defined
    public static Int128 operator checked +(Int128 lhs, Int128 rhs);
    public static Int128 operator +(Int128 lhs, Int128 rhs);

    // This is fine, only a regular operator is defined
    public static Int128 operator -(Int128 lhs, Int128 rhs);

    // This should error, a regular operator must also be defined
    public static Int128 operator checked *(Int128 lhs, Int128 rhs);
}

Aday kullanıcı tanımlı işleçler

Aday kullanıcı işleçleri (§12.4.6) bölümü aşağıdaki gibi ayarlanır (eklemeler/değişiklikler kalın yazı tipindedir).

bir tür T ve operator op(A)bir işlem verildiğinde, burada op aşırı yüklenebilir bir işleç ve A bir bağımsız değişken listesidir, T için operator op(A) tarafından sağlanan aday kullanıcı tanımlı işleç kümesi aşağıdaki gibi belirlenir:

  • T0türünü belirleyin. T nullable bir türse, T0 onun temel türüdür, değilse T0T'e eşittir.
  • Ukullanıcı tanımlı işleç kümesini bulun. Bu küme şunlardan oluşur:
    • unchecked değerlendirme bağlamında, operator opiçindeki tüm normal T0 bildirimleri.
    • checked değerlendirme bağlamında, çift olarak eşleşen operator op bildirimine sahip normal bildirimler dışında, T0 içindeki tüm denetlenen ve normal checked operator bildirimleri.
  • operator op'deki tüm U bildirimleri ve bu tür işleçlerin kaldırılan tüm biçimleri için, bağımsız değişken listesiyle ilgili olarak en az bir işleç (A) varsa, aday işleçler kümesi T0'deki tüm ilgili işleçlerden oluşur.
  • Aksi takdirde, T0objectise, aday işleç kümesi boş olur.
  • Aksi takdirde, T0 tarafından sağlanan aday işleçler kümesi, T0doğrudan temel sınıfı veya T0 bir tür parametresiyse T0 etkin temel sınıfı tarafından sağlanan aday işleçler kümesidir.

https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfacesarabirimlerdeki aday işleçler kümesi belirlenirken de benzer kurallar uygulanır.

§12.8.20 bölümü, işaretli/işaretsiz bağlamın birli ve ikili işleç aşırı yükleme çözümlemesi üzerindeki etkisini yansıtacak şekilde ayarlanır.

Örnek 1:

public class MyClass
{
    public static void Add(Int128 lhs, Int128 rhs)
    {
        // Resolves to `op_CheckedAddition`
        Int128 r1 = checked(lhs + rhs);

        // Resolves to `op_Addition`
        Int128 r2 = unchecked(lhs + rhs);

        // Resolve to `op_Subtraction`
        Int128 r3 = checked(lhs - rhs);

        // Resolve to `op_Subtraction`
        Int128 r4 = unchecked(lhs - rhs);

        // Resolves to `op_CheckedMultiply`
        Int128 r5 = checked(lhs * rhs);

        // Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
        Int128 r6 = unchecked(lhs * rhs);
    }

    public static void Divide(Int128 lhs, byte rhs)
    {
        // Resolves to `op_Division` - it is a better match than `op_CheckedDivision`
        Int128 r4 = checked(lhs / rhs);
    }
}

public struct Int128
{
    public static Int128 operator checked +(Int128 lhs, Int128 rhs);
    public static Int128 operator +(Int128 lhs, Int128 rhs);

    public static Int128 operator -(Int128 lhs, Int128 rhs);

    // Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
    public static Int128 operator checked *(Int128 lhs, Int128 rhs);

    public static Int128 operator checked /(Int128 lhs, int rhs);

    public static Int128 operator /(Int128 lhs, byte rhs);
}

Örnek 2:

class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // error CS0034: Operator '+' is ambiguous on operands of type 'C2' and 'C3'
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    // Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
    public static C1 operator checked + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    public static C2 operator + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}

Örnek 3:

class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // error CS0034: Operator '+' is ambiguous on operands of type 'C2' and 'C3'
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    // Cannot be declared in C# - missing unchecked operator, but could be declared by some other language
    public static C2 operator checked + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}

Dönüştürme işleçleri

Dönüşüm checked operators, §15.10.4'de belirtilen kurallara uyar.

Ancak, checked operator bildirimi için çift olarak regular operatorbildirimi gerekir. Aksi takdirde derleme zamanı hatası oluşur.

Aşağıdaki paragraf

Dönüştürme işlecinin imzası kaynak türünden ve hedef türünden oluşur. (Bu, iade türünün imzaya katıldığı tek üye biçimidir.) Dönüştürme işlecinin örtük veya açık sınıflandırması, işlecin imzasının bir parçası değildir. Bu nedenle, bir sınıf veya yapı aynı kaynak ve hedef türlerine sahip örtük ve açık dönüştürme işlecini bildiremez.

, bir türün aynı kaynak ve hedef türlerle işaretli ve normal açık dönüştürme biçimlerini bildirmesine izin verecek şekilde ayarlanır. Bir türün hem örtük hem de aynı kaynak ve hedef türlerine sahip denetlenen açık dönüştürme işlecini bildirmesine izin verilmez.

Kullanıcı tanımlı açık dönüştürmelerin işlenmesi

§10.5.5üçüncü madde işareti:

  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, D sınıfları veya yapıları tarafından belirtilen ve S'in kapsadığı veya kapsandığı bir türden, T'nin kapsadığı veya kapsandığı bir türe dönüştüren kullanıcı tanımlı ve kaldırılan örtük veya açık dönüştürme operatörlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

aşağıdaki madde işareti noktalarıyla değiştirilecektir:

  • U0dönüştürme işleçleri kümesini bulun. Bu küme şunlardan oluşur:
    • unchecked değerlendirme bağlamında, Dsınıflar veya yapılar tarafından bildirilen kullanıcı tanımlı örtük veya normal açık dönüştürme işleçleri kullanılır.
    • checked değerlendirme bağlamında, D sınıflar veya yapılar tarafından bildirilen kullanıcı tanımlı örtük veya normal/denetlenmiş açık dönüştürme işleçleri, aynı bildirim türü içindeki çift yönlü eşleştirme checked operator bildirimine sahip normal açık dönüştürme işleçleri dışında.
  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, tarafından kapsadığı veya tarafından kapsadığı bir türe dönüştüren kullanıcı tanımlı ve kaldırılmış örtük veya açık dönüştürme işleçlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

§11.8.20 İşaretli ve işaretlenmemiş işleçler bölümü, denetlenen/işaretlenmemiş bağlamın kullanıcı tanımlı açık dönüştürmelerin işlenmesi üzerindeki etkisini yansıtacak şekilde ayarlanır.

İşleçlerin uygulanması

checked operator bir regular operator uygulamaz ve tam tersi de geçerlidir.

Linq İfade Ağaçları

Checked operators Linq İfade Ağaçlarında desteklenecektir. İlgili UnaryExpressionile bir /BinaryExpressionMethodInfo düğümü oluşturulacak. Aşağıdaki fabrika yöntemleri kullanılacaktır:

public static UnaryExpression NegateChecked (Expression expression, MethodInfo? method);

public static BinaryExpression AddChecked (Expression left, Expression right, MethodInfo? method);
public static BinaryExpression SubtractChecked (Expression left, Expression right, MethodInfo? method);
public static BinaryExpression MultiplyChecked (Expression left, Expression right, MethodInfo? method);

public static UnaryExpression ConvertChecked (Expression expression, Type type, MethodInfo? method);

C# ifadesinin ifade ağaçlarındaki atamaları desteklemediğini, bu nedenle işaretli artış/azaltmanın da desteklenmediğini unutmayın.

Kontrollü bölme için fabrika yöntemi yoktur. Bununla ilgili açık bir soru var - Linq İfade Ağaçlarında Denetlenen Bölüm.

Dinamik

CoreCLR'de dinamik çağrıda kontrollü operatörler için destek eklemenin maliyetini araştıracağız ve maliyet çok yüksek değilse bir uygulama gerçekleştireceğiz. Bu, https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md'den bir alıntıdır.

Dezavantaj -ları

Bu, dile ekstra karmaşıklık ekler ve kullanıcıların türlerinde çeşitli geriye dönük uyumsuz değişiklikler yapmasına olanak tanır.

Alternatif

Kitaplıkların kullanıma sunmayı planladığı genel matematik arabirimleri adlandırılmış yöntemleri (AddCheckedgibi) kullanıma açabilir. Birincil dezavantajı, bunun daha az okunabilir/sürdürülebilir olması ve işleçler etrafında dil önceliği kurallarından yararlanmamasıdır.

Bu bölümde ele alınan ancak uygulanmayan alternatifler listelenmiştir

checked anahtar sözcüğünü yerleştirme

Alternatif olarak checked anahtar sözcüğü, operator anahtar sözcüğünden hemen önceki yere taşınabilir:

public static T checked operator ++(T x) {...}
public static T checked operator --(T x) {...}
public static T checked operator -(T x) {...}
public static T checked operator +(T lhs, T rhs) {...}
public static T checked operator -(T lhs, T rhs) {...}
public static T checked operator *(T lhs, T rhs) {...}
public static T checked operator /(T lhs, T rhs) {...}
public static explicit checked operator U(T x) {...}
public static T checked I1.operator ++(T x) {...}
public static T checked I1.operator --(T x) {...}
public static T checked I1.operator -(T x) {...}
public static T checked I1.operator +(T lhs, T rhs) {...}
public static T checked I1.operator -(T lhs, T rhs) {...}
public static T checked I1.operator *(T lhs, T rhs) {...}
public static T checked I1.operator /(T lhs, T rhs) {...}
public static explicit checked I1.operator U(T x) {...}

Veya işleç değiştirici kümesine taşınabilir:

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | 'checked'
    | operator_modifier_unsafe
    ;
public static checked T operator ++(T x) {...}
public static checked T operator --(T x) {...}
public static checked T operator -(T x) {...}
public static checked T operator +(T lhs, T rhs) {...}
public static checked T operator -(T lhs, T rhs) {...}
public static checked T operator *(T lhs, T rhs) {...}
public static checked T operator /(T lhs, T rhs) {...}
public static checked explicit operator U(T x) {...}
public static checked T I1.operator ++(T x) {...}
public static checked T I1.operator --(T x) {...}
public static checked T I1.operator -(T x) {...}
public static checked T I1.operator +(T lhs, T rhs) {...}
public static checked T I1.operator -(T lhs, T rhs) {...}
public static checked T I1.operator *(T lhs, T rhs) {...}
public static checked T I1.operator /(T lhs, T rhs) {...}
public static checked explicit I1.operator U(T x) {...}

unchecked anahtar sözcüğü

aşağıdaki olası anlamlara sahip unchecked anahtar sözcüğüyle aynı konumda checked anahtar sözcüğü desteklemeye yönelik öneriler vardı:

  • Yalnızca operatörün normal doğasını açıkça yansıtmak veya
  • Belki de unchecked bağlamında kullanılması gereken bir işlecin farklı bir türünü tanımlamaktır. Dil, yıkıcı değişikliklerin sayısını sınırlamaya yardımcı olmak için op_Addition, op_CheckedAdditionve op_UncheckedAddition'yi destekleyebilir. Bu, çoğu kodda gerekli olmayabilecek başka bir karmaşıklık katmanı ekler.

ECMA-335'te işleç adları

Alternatif olarak, işleç adları op_UnaryNegationChecked, op_AdditionChecked, op_SubtractionChecked, op_MultiplyChecked, op_DivisionCheckedolabilir ve sonunda Checked bulunabilir. Ancak, adları işleç sözcüğüyle sonlandırmak için oluşturulmuş bir desen zaten var gibi görünüyor. Örneğin, op_UnsignedRightShift işleci yerine bir op_RightShiftUnsigned işleci vardır.

Checked operators bir unchecked bağlamında kullanılamaz

Derleyici, unchecked bağlamında üye bulma işlemi yaparken kullanıcı tanımlı aday işleçleri bulmak için checked operators'i yoksayabilir. Yalnızca bir checked operatortanımlayan meta verilerle karşılaşılırsa bir derleme hatası oluşur.

public class MyClass
{
    public static void Add(Int128 lhs, Int128 rhs)
    {
        // Resolves to `op_CheckedMultiply`
        Int128 r5 = checked(lhs * rhs);

        // Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
        Int128 r5 = unchecked(lhs * rhs);
    }
}

public struct Int128
{
    public static Int128 operator checked *(Int128 lhs, Int128 rhs);
}

checked bağlamında daha karmaşık işleç arama ve aşırı yükleme çözümleme kuralları

Derleyici, checked bağlamında kullanıcı tanımlı aday işleçleri bulmak için üye araması gerçekleştirirken Checkedile biten geçerli işleçleri de dikkate alır. Başka bir ifadeyle, derleyici ikili ekleme işleci için uygun işlev üyelerini bulmaya çalışırsa hem op_Addition hem de op_AdditionCheckedarar. Geçerli tek işlev üyesi bir checked operatorise, o kullanılacaktır. Hem regular operator hem de checked operator varsa ve eşit oranda uygulanabilirse checked operator tercih edilir. Hem regular operator hem de checked operator varsa ama regular operator tam olarak eşleşiyorsa ancak checked operator eşleşmediyse, derleyici regular operatortercih eder.

public class MyClass
{
    public static void Add(Int128 lhs, Int128 rhs)
    {
        // Resolves to `op_CheckedAddition`
        Int128 r1 = checked(lhs + rhs);

        // Resolves to `op_Addition`
        Int128 r2 = unchecked(lhs + rhs);

        // Resolve to `op_Subtraction`
        Int128 r3 = checked(lhs - rhs);

        // Resolve to `op_Subtraction`
        Int128 r4 = unchecked(lhs - rhs);
    }

    public static void Multiply(Int128 lhs, byte rhs)
    {
        // Resolves to `op_Multiply` even though `op_CheckedMultiply` is also applicable
        Int128 r4 = checked(lhs * rhs);
    }
}

public struct Int128
{
    public static Int128 operator checked +(Int128 lhs, Int128 rhs);
    public static Int128 operator +(Int128 lhs, Int128 rhs);

    public static Int128 operator -(Int128 lhs, Int128 rhs);

    public static Int128 operator checked *(Int128 lhs, int rhs);
    public static Int128 operator *(Int128 lhs, byte rhs);
}

Aday kullanıcı tanımlı işleçler kümesini oluşturmanın bir başka yolu daha

Tekli işleç aşırı yükleme çözümlemesi

regular operator unchecked değerlendirme bağlamıyla eşleşirse, checked operatorchecked değerlendirme bağlamıyla eşleşirse ve checked formuna sahip olmayan bir operatör (örneğin, +) her iki bağlamla da eşleşirse, §12.4.4 - Tekli işleç aşırı yükleme çözümlemesi'ndeki ilk madde işareti:

aşağıdaki iki madde işaretiyle değiştirilir:

  • İşlem Xoperator op(x) eşleşmesi için tarafından sağlanan aday kullanıcı tanımlı işleçler kümesi, Aday kullanıcı tanımlı işleçlerin kuralları kullanılarak belirlenir.
  • Kullanıcı tanımlı aday işleç kümesi boş değilse, bu işlem için aday işleç kümesi olur. Aksi takdirde, X tarafından, operator op(x)işlemi için karşıt kontrol edilmiş/kontrol edilmemiş bağlam'e uyan aday kullanıcı tanımlı işleçler kümesi, §12.4.6 - Aday kullanıcı tanımlı işleçlerkuralları kullanılarak belirlenir.

İkili işleç aşırı yükleme çözümlemesi

regular operator unchecked değerlendirme bağlamıyla eşleşirse, checked operatorchecked değerlendirme bağlamı ile eşleşir ve checked formu olmayan bir işleç (örneğin, %) her iki bağlamla da eşleşir; içindeki ilk madde işareti §12.4.5 - İkili işleç aşırı yükleme çözümlemesi:

  • İşlem X için Y ve operator op(x,y) tarafından sağlanan aday kullanıcı tanımlı işleç kümesi belirlenir. Küme, her biri Xkuralları kullanılarak belirlenen, Y tarafından sağlanan aday işleçleri ile tarafından sağlanan aday işleçlerinin birleşiminden oluşur. X ve Y aynı türdeyse veya X ve Y ortak bir temel türden türetilmişse, paylaşılan aday işleçleri birleştirilmiş kümede yalnızca bir kez gerçekleşir.

aşağıdaki iki madde işaretiyle değiştirilir:

  • geçerli işaretli/işaretsiz bağlamına uyum sağlayan işlemi için ve tarafınca sağlanan aday kullanıcı tanımlı işleçler kümesi belirlenir. Küme, her biri Xkuralları kullanılarak belirlenen, Y tarafından sağlanan aday işleçleri ile tarafından sağlanan aday işleçlerinin birleşiminden oluşur. X ve Y aynı türdeyse veya X ve Y ortak bir temel türden türetilmişse, paylaşılan aday işleçleri birleştirilmiş kümede yalnızca bir kez gerçekleşir.
  • Kullanıcı tanımlı aday işleç kümesi boş değilse, bu işlem için aday işleç kümesi olur. Aksi takdirde, kontrol edilen/kontrolsüz bağlam ile eşleşen operasyon için ve tarafından sağlanan aday kullanıcı tanımlı işleçler kümesi belirlenir. Küme, her biri Xkuralları kullanılarak belirlenen, Y tarafından sağlanan aday işleçleri ile tarafından sağlanan aday işleçlerinin birleşiminden oluşur. X ve Y aynı türdeyse veya X ve Y ortak bir temel türden türetilmişse, paylaşılan aday işleçleri birleştirilmiş kümede yalnızca bir kez gerçekleşir.
Örnek 1:
public class MyClass
{
    public static void Add(Int128 lhs, Int128 rhs)
    {
        // Resolves to `op_CheckedAddition`
        Int128 r1 = checked(lhs + rhs);

        // Resolves to `op_Addition`
        Int128 r2 = unchecked(lhs + rhs);

        // Resolve to `op_Subtraction`
        Int128 r3 = checked(lhs - rhs);

        // Resolve to `op_Subtraction`
        Int128 r4 = unchecked(lhs - rhs);

        // Resolves to `op_CheckedMultiply`
        Int128 r5 = checked(lhs * rhs);

        // Resolves to `op_CheckedMultiply`
        Int128 r5 = unchecked(lhs * rhs);
    }

    public static void Divide(Int128 lhs, byte rhs)
    {
        // Resolves to `op_CheckedDivision`
        Int128 r4 = checked(lhs / rhs);
    }
}

public struct Int128
{
    public static Int128 operator checked +(Int128 lhs, Int128 rhs);
    public static Int128 operator +(Int128 lhs, Int128 rhs);

    public static Int128 operator -(Int128 lhs, Int128 rhs);

    public static Int128 operator checked *(Int128 lhs, Int128 rhs);

    public static Int128 operator checked /(Int128 lhs, int rhs);
    public static Int128 operator /(Int128 lhs, byte rhs);
}
Örnek 2:
class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // C1.op_CheckedAddition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator checked + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    public static C2 operator + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}
Örnek 3:
class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // C2.op_CheckedAddition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    public static C2 operator checked + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}
Örnek #4:
class C
{
    static void Add(C2 x, byte y)
    {
        object o;
        
        // C1.op_CheckedAddition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }

    static void Add2(C2 x, int y)
    {
        object o;
        
        // C2.op_Addition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator checked + (C1 x, byte y) => new C1();
}

class C2 : C1
{
    public static C2 operator + (C2 x, int y) => new C2();
}
Örnek #5:
class C
{
    static void Add(C2 x, byte y)
    {
        object o;
        
        // C2.op_CheckedAddition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }

    static void Add2(C2 x, int y)
    {
        object o;
        
        // C1.op_Addition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator + (C1 x, int y) => new C1();
}

class C2 : C1
{
    public static C2 operator checked + (C2 x, byte y) => new C2();
}

Kullanıcı tanımlı açık dönüştürmelerin işlenmesi

regular operator unchecked değerlendirme bağlamı ile ve checked operatorchecked değerlendirme bağlamı ile eşleştiğini varsayarsak, kullanıcı tanımlı dönüştürmelerin değerlendirilmesi §10.5.3 içindeki üçüncü madde:

  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, D sınıfları veya yapıları tarafından belirtilen ve S'in kapsadığı veya kapsandığı bir türden, T'nin kapsadığı veya kapsandığı bir türe dönüştüren kullanıcı tanımlı ve kaldırılan örtük veya açık dönüştürme operatörlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

aşağıdaki madde işareti noktalarıyla değiştirilecektir:

  • geçerli işaretli/işaretsiz bağlamla eşleşen geçerli kullanıcı tanımlı ve kaldırılmış açık dönüştürme işleçleri kümesini bulun. Bu küme, D sınıfları veya yapıları tarafından bildirilen ve geçerli işaretli/işaretsiz bağlam eşleşen ve S tarafından kapsayan veya Tkapsayan bir türe dönüştüren kullanıcı tanımlı ve kaldırılmış açık dönüştürme işleçlerinden oluşur.
  • Kullanıcı tanımlı ve kaldırılmış açık dönüştürme işleçlerinden, karşıt denetimli/denetimsiz bağlama uyan kümesini bulun ,, U1. U0 boş değilse U1 boş olur. Aksi takdirde, bu küme, D sınıfları veya yapıları tarafından bildirilen kullanıcı tanımlı ve yükseltilmiş açık dönüştürme işleçlerinden oluşur ve zıt işaretli/işaretsiz bağlam ile eşleşir ve S tarafından kapsanan veya onları kapsayan bir türden Ttarafından kapsanan veya onları kapsayan bir türe dönüştürülür.
  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, U0, U1ve D sınıfları veya yapıları tarafından bildirilen ve S ile kapsanan veya Tile kapsayan bir türe dönüştüren kullanıcı tanımlı ve kaldırılmış örtük dönüştürme operatörlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

Aday kullanıcı tanımlı işleçler kümesini oluşturmanın bir başka başka yolu daha

Tekli işleç aşırı yükleme çözümlemesi

§12.4.4 bölümdeki ilk madde işareti aşağıdaki gibi ayarlanır (eklemeler kalın olarak gösterilir).

  • İşlem X için operator op(x) tarafından sağlanan aday kullanıcı tanımlı işleç kümesi, aşağıdaki "Aday kullanıcı tanımlı işleçler" bölümü kuralları kullanılarak belirlenir. Küme, işaretli formda en az bir işleç içeriyorsa, normal formdaki tüm işleçler kümeden kaldırılır.

§12.8.20 bölümü, işaretli/işaretsiz bağlamın birli işleç aşırı yükleme çözünürlüğü üzerindeki etkisini yansıtacak şekilde ayarlanacaktır.

İkili işleç aşırı yükleme çözümlemesi

§12.4.5 içinde bölümündeki ilk madde işareti aşağıdaki şekilde ayarlanacaktır (eklemeler kalın harfle belirtilecektir).

  • İşlem X için Y ve operator op(x,y) tarafından sağlanan aday kullanıcı tanımlı işleç kümesi belirlenir. Küme, X tarafından sağlanan aday işleçleri ile Ytarafından sağlanan aday işleçlerinin birleşiminden oluşur ve her biri aşağıdaki "Aday kullanıcı tanımlı işleçler" bölümü kullanılarak belirlenir. X ve Y aynı türdeyse veya X ve Y ortak bir temel türden türetilmişse, paylaşılan aday işleçleri birleştirilmiş kümede yalnızca bir kez gerçekleşir. Küme, işaretli formda en az bir işleç içeriyorsa, normal formdaki tüm işleçler kümeden kaldırılır.

İşaretli ve işaretlenmemiş işleçler §12.8.20 bölümü, denetlenen/denetlenmeyen bağlamın ikili işleç aşırı yükleme çözümlemesi üzerindeki etkisini yansıtacak şekilde ayarlanır.

Aday kullanıcı tanımlı işleçler

§12.4.6 - Aday kullanıcı tanımlı işleçler bölümü aşağıdaki gibi ayarlanır (eklemeler kalındır).

bir tür T ve operator op(A)bir işlem verildiğinde, burada op aşırı yüklenebilir bir işleç ve A bir bağımsız değişken listesidir, T için operator op(A) tarafından sağlanan aday kullanıcı tanımlı işleç kümesi aşağıdaki gibi belirlenir:

  • T0türünü belirleyin. T nullable bir türse, T0 onun temel türüdür, değilse T0T'e eşittir.
  • Tüm bildirimleri, değerlendirme bağlamında ve yalnızca değerlendirme bağlamındaki normal formlarında ve bu işleçlerin kaldırılan tüm formlarında, denetlenen ve normal formlarında , bağımsız değişken listesiyle ilgili olarak en az bir işleç (§12.6.4.2) geçerliyse, aday işleçler kümesi içindeki bu tür geçerli işleçlerden oluşur.
  • Aksi takdirde, T0objectise, aday işleç kümesi boş olur.
  • Aksi takdirde, T0 tarafından sağlanan aday işleçler kümesi, T0doğrudan temel sınıfı veya T0 bir tür parametresiyse T0 etkin temel sınıfı tarafından sağlanan aday işleçler kümesidir.

https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-06-27.md#shadowing-within-interfacesarabirimlerdeki aday işleçler kümesi belirlenirken benzer filtreleme uygulanır.

§12.8.20 bölümü, işaretli/işaretsiz bağlamın birli ve ikili işleç aşırı yükleme çözümlemesi üzerindeki etkisini yansıtacak şekilde ayarlanır.

Örnek 1:
public class MyClass
{
    public static void Add(Int128 lhs, Int128 rhs)
    {
        // Resolves to `op_CheckedAddition`
        Int128 r1 = checked(lhs + rhs);

        // Resolves to `op_Addition`
        Int128 r2 = unchecked(lhs + rhs);

        // Resolve to `op_Subtraction`
        Int128 r3 = checked(lhs - rhs);

        // Resolve to `op_Subtraction`
        Int128 r4 = unchecked(lhs - rhs);

        // Resolves to `op_CheckedMultiply`
        Int128 r5 = checked(lhs * rhs);

        // Error: Operator '*' cannot be applied to operands of type 'Int128' and 'Int128'
        Int128 r5 = unchecked(lhs * rhs);
    }

    public static void Divide(Int128 lhs, byte rhs)
    {
        // Resolves to `op_CheckedDivision`
        Int128 r4 = checked(lhs / rhs);
    }
}

public struct Int128
{
    public static Int128 operator checked +(Int128 lhs, Int128 rhs);
    public static Int128 operator +(Int128 lhs, Int128 rhs);

    public static Int128 operator -(Int128 lhs, Int128 rhs);

    public static Int128 operator checked *(Int128 lhs, Int128 rhs);

    public static Int128 operator checked /(Int128 lhs, int rhs);
    public static Int128 operator /(Int128 lhs, byte rhs);
}
Örnek 2:
class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // C1.op_CheckedAddition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator checked + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    public static C2 operator + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}
Örnek 3:
class C
{
    static void Add(C2 x, C3 y)
    {
        object o;
        
        // C2.op_CheckedAddition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator + (C1 x, C3 y) => new C3();
}

class C2 : C1
{
    public static C2 operator checked + (C2 x, C1 y) => new C2();
}

class C3 : C1
{
}
Örnek #4:
class C
{
    static void Add(C2 x, byte y)
    {
        object o;
        
        // C2.op_Addition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }

    static void Add2(C2 x, int y)
    {
        object o;
        
        // C2.op_Addition
        o = checked(x + y);
        
        // C2.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator checked + (C1 x, byte y) => new C1();
}

class C2 : C1
{
    public static C2 operator + (C2 x, int y) => new C2();
}
Örnek #5:
class C
{
    static void Add(C2 x, byte y)
    {
        object o;
        
        // C2.op_CheckedAddition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }

    static void Add2(C2 x, int y)
    {
        object o;
        
        // C1.op_Addition
        o = checked(x + y);
        
        // C1.op_Addition
        o = unchecked(x + y);
    }
}

class C1
{
    public static C1 operator + (C1 x, int y) => new C1();
}

class C2 : C1
{
    public static C2 operator checked + (C2 x, byte y) => new C2();
}

Kullanıcı tanımlı açık dönüştürmelerin işlenmesi

§10.5.5üçüncü madde işareti:

  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, D sınıfları veya yapıları tarafından belirtilen ve S'in kapsadığı veya kapsandığı bir türden, T'nin kapsadığı veya kapsandığı bir türe dönüştüren kullanıcı tanımlı ve kaldırılan örtük veya açık dönüştürme operatörlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

aşağıdaki madde işareti noktalarıyla değiştirilecektir:

  • geçerli kullanıcı tanımlı ve yükseltilmiş açık dönüştürme işleçleri kümesini bulun U0. Bu küme, sınıflar veya yapılar tarafından D değerlendirme bağlamında denetlenen ve normal formlarında checkedbildirilen kullanıcı tanımlı ve kaldırılmış açık dönüştürme işleçlerinden oluşur ve yalnızca unchecked değerlendirme bağlamında normal biçimlerinde ve S tarafından kapsadığı veya Ttarafından kapsadığı bir türe dönüştürülür.
  • U0 işaretli formda en az bir işleç içeriyorsa, normal formdaki tüm işleçler kümeden kaldırılır.
  • Kullanıcı tanımlı ve kaldırılmış geçerli dönüşüm operatörleri kümesini bul U. Bu küme, U0işleçlerinden ve D sınıfları veya yapıları tarafından bildirilen, S'yi kapsayan veya ondan kapsanan bir tipten, T'ü kapsayan veya ondan kapsanan bir türe dönüştüren, kullanıcı tanımlı ve yükseltilmiş örtük dönüştürme işleçlerinden oluşur. U boşsa, dönüştürme tanımsız olur ve derleme zamanı hatası oluşur.

§12.8.20 İşaretli ve işaretsiz işleçler bölümü, denetlenen/işaretlenmemiş bağlamın kullanıcı tanımlı açık dönüştürmelerin işlenmesi üzerindeki etkisini yansıtacak şekilde ayarlanır.

bir checked operator içinde kontrollü ve kontrolsüz bağlam karşılaştırması

Derleyici, bir checked operator varsayılan bağlamını kontrollü olarak değerlendirebilir. Eğer algoritmasının bir bölümü unchecked'e katılmaması gerekiyorsa, geliştiricinin checked context'ı açıkça kullanması gerekir. Ancak, checked/unchecked belirteçlerin işleçlerde düzenleyici olarak gövde içindeki bağlamı ayarlamasına izin vermeye başlarsak bu işlem gelecekte iyi çalışmayabilir. Değiştirici ve anahtar sözcük birbiriyle çelişebilir. Ayrıca, bir regular operator için aynı işlemi (varsayılan bağlamı işaretlenmemiş olarak kabul et) yapamayız çünkü bu uyumsuzluk sorununa yol açar.

Çözülmemiş sorular

Dil, yöntemlerde checked ve unchecked değiştiricilere izin vermelidir (örn. static checked void M())? Bu, bunu gerektiren yöntemler için iç içe yerleştirme düzeylerinin kaldırılmasına izin verir.

Linq Expression Trees'de kontrol edilmiş bölüm

Denetimli bir bölme düğümü oluşturmak için bir fabrika yöntemi yoktur ve ExpressionType.DivideChecked üyesi de yoktur. MethodInfo yöntemine işaret eden op_CheckedDivision düzenli bölme düğümü oluşturmak için aşağıdaki fabrika yöntemini kullanmaya devam edebiliriz. Tüketicilerin bağlamı çıkarsamak için adı kontrol etmesi gerekir.

public static BinaryExpression Divide (Expression left, Expression right, MethodInfo? method);

§12.8.20 bölümünde işaretli/işaretsiz değerlendirme bağlamından etkilenen işleçlerden biri olarak / işleci listelense de IL'nin işaretli bölme gerçekleştirmek için özel bir işlem kodu yoktur. Derleyici, bugün bağlamdan bağımsız olarak her zaman fabrika metodunu kullanır.

Teklifi: Denetlenen kullanıcı tanımlı devision, Linq expression Trees içinde desteklenmeyecektir.

(Çözüldü) Örtük kontrollü dönüştürme işleçlerini desteklemeli miyiz?

Genel olarak, örtük dönüştürme işleçlerinin istisna fırlatması beklenmez.

Teklifi: Hayır.

Çözümü: Onaylandı - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md#checked-implicit-conversions

Tasarım toplantıları

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-07.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-09.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-14.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-23.md