Aracılığıyla paylaş


15 Sınıf

15.1 Genel

Sınıf, veri üyeleri (sabitler ve alanlar), işlev üyeleri (yöntemler, özellikler, olaylar, dizin oluşturucular, işleçler, örnek oluşturucuları, sonlandırıcılar ve statik oluşturucular) ve iç içe geçmiş türler içerebilen bir veri yapısıdır. Sınıf türleri, bir türetilmiş sınıfın temel sınıfı genişletip özelleştirebileceği bir mekanizma olan kalıtımı destekler.

Yapılar (§16) ve arabirimler (§18) sınıflara benzer ancak belirli kısıtlamalara sahip üyelere sahiptir. Bu yan tümce, sınıflar ve sınıf üyeleri için bildirimleri tanımlar. Yapılar ve arabirimler için yan tümceler, sınıf türlerindeki ilgili bildirimler açısından bu türler için kısıtlamaları tanımlar.

15.2 Sınıf bildirimleri

15.2.1 Genel

class_declaration, yeni bir sınıf bildiren bir type_declaration (§14.7) olur.

class_declaration
    : attributes? class_modifier* 'partial'? 'class' identifier
        type_parameter_list? class_base? type_parameter_constraints_clause*
        class_body ';'?
    ;

class_declaration isteğe bağlı bir öznitelik kümesi (§23) ve ardından isteğe bağlı bir class_modifierkümesi (§15.2.2) ve ardından isteğe bağlı partial değiştirici (§15.2.7) ve ardından anahtar sözcüğü class ve sınıfı adlandıran bir tanımlayıcıdan oluşur. ardından isteğe bağlı bir type_parameter_list (§15.2.3) ve ardından isteğe bağlı bir class_base belirtimi (§15.2.4) ve ardından isteğe bağlı bir küme type_parameter_constraints_clause(§15.2.5) ve ardından class_body (§15.2.6) isteğe bağlı olarak noktalı virgül izler.

Sınıf bildirimi, type_parameter_list sağlamadığı takdirde type_parameter_constraints_clause sağlamamalıdır.

Atype_parameter_list sağlayan bir sınıf bildirimi, genel bir sınıf bildirimidir. Ayrıca, genel bir sınıf bildiriminin veya genel yapı bildiriminin içinde iç içe yerleştirilmiş tüm sınıflar kendisi genel bir sınıf bildirimidir, çünkü içeren tür için tür bağımsız değişkenleri, oluşturulmuş bir türü oluşturmak amacıyla sağlanmalıdır. (§8.4).

15.2.2 Sınıf değiştiricileri

15.2.2.1 Genel

class_declaration isteğe bağlı olarak bir sınıf değiştirici dizisi içerebilir:

class_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'abstract'
    | 'sealed'
    | 'static'
    | unsafe_modifier   // unsafe code support
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

Aynı değiştiricinin bir sınıf bildiriminde birden çok kez görünmesi derleme zamanı hatasıdır.

Değiştiriciye new iç içe sınıflarda izin verilir. Sınıfın, aynı ada sahip devralınan bir üyeyi §15.3.5'te açıklandığı gibi gizlediğini belirtir. Değiştiricinin new iç içe sınıf bildirimi olmayan bir sınıf bildiriminde görünmesi derleme zamanı hatasıdır.

public, protected, internalve private değiştiricileri sınıfın erişilebilirliğini denetler. Sınıf bildiriminin gerçekleştiği bağlama bağlı olarak, bu değiştiricilerden bazılarına izin verilmeyebilir (§7.5.2).

Kısmi tür bildirimi (§15.2.7) bir erişilebilirlik belirtimi (, public, protectedve internal değiştiricileri aracılığıylaprivate) içerdiğinde, bu belirtim erişilebilirlik belirtimi içeren diğer tüm bölümleri kabul eder. Kısmi türün hiçbir bölümü erişilebilirlik belirtimi içermiyorsa, türe uygun varsayılan erişilebilirlik (§7.5.2) verilir.

abstract, sealedve static değiştiricileri aşağıdaki alt başlıklarda açıklandı.

15.2.2.2 Soyut sınıflar

abstract Değiştirici, bir sınıfın eksik olduğunu ve yalnızca temel sınıf olarak kullanılmasının amaçlandığını belirtmek için kullanılır. Soyut sınıf, soyut olmayan bir sınıftan aşağıdaki yollarla farklıdır:

  • Soyut bir sınıfın örneği doğrudan oluşturulamaz ve new işlecini bir soyut sınıfta kullanmak derleme zamanı hatasına neden olur. Derleme zamanı türleri soyut olan değişkenlerin ve değerlerin olması mümkün olsa da, bu tür değişkenler ve değerler mutlaka null olacak veya soyut türlerden türetilen soyut olmayan sınıfların örneklerine başvurular içerecektir.
  • Soyut bir sınıfın soyut üyeler içermesine izin verilir (ancak gerekli değildir).
  • Soyut bir sınıf kapatılamaz.

Soyut olmayan bir sınıf soyut bir sınıftan türetildiğinde, soyut olmayan sınıf devralınan tüm soyut üyelerin gerçek uygulamalarını içerecek ve böylece bu soyut üyeleri geçersiz kılacaktır.

Örnek: Aşağıdaki kodda

abstract class A
{
    public abstract void F();
}

abstract class B : A
{
    public void G() {}
}

class C : B
{
    public override void F()
    {
        // Actual implementation of F
    }
}

soyut sınıfı A bir soyut yöntemi Ftanıtır. Sınıf B, ek bir yöntem olan G tanıtır; ancak F öğesinin bir uygulamasını sağlamadığı için, B da soyut olarak bildirilmelidir. Sınıf C geçersiz kılar F ve gerçek bir uygulama sağlar. C içinde soyut üye olmadığından, C'in soyut olmamasına izin verilir (ancak bu gerekli değildir).

son örnek

Bir sınıfın kısmi tür bildiriminin (§15.2.7) bir veya daha fazla bölümü değiştiriciyi abstract içerirse, sınıf soyut olur. Aksi takdirde, sınıf soyut değildir.

15.2.2.3 Korumalı sınıflar

sealed Değiştirici, bir sınıftan türetilmesini önlemek için kullanılır. Korumalı bir sınıf başka bir sınıfın temel sınıfı olarak belirtilirse derleme zamanı hatası oluşur.

Mühürlenmiş sınıf soyut bir sınıf da olamaz.

Not: Değiştirici sealed öncelikli olarak istenmeyen türetmeleri önlemek için kullanılır, ancak belirli çalışma zamanı iyileştirmelerini de etkinleştirir. Özellikle, korumalı bir sınıfın hiçbir zaman türetilmiş sınıfı olmadığı bilindiğinden, korumalı sınıf örneklerindeki sanal işlev üyesi çağrılarını sanal olmayan çağrılara dönüştürmek mümkündür. son not

Bir sınıfın kısmi tür bildiriminin (§15.2.7) bir veya daha fazla bölümü değiştiriciyi sealed içerirse, sınıf mühürlenir. Aksi takdirde, sınıf açılır.

15.2.2.4 Statik sınıflar

15.2.2.4.1 Genel

static Değiştirici, statik sınıf olarak bildirilen sınıfı işaretlemek için kullanılır. Statik sınıf örneği oluşturulmayacak, tür olarak kullanılmayacaktır ve yalnızca statik üyeler içermelidir. Yalnızca statik bir sınıf uzantı yöntemlerinin bildirimlerini içerebilir (§15.6.10).

Statik sınıf bildirimi aşağıdaki kısıtlamalara tabidir:

  • Statik bir sınıf, sealed veya abstract değiştiricisi içermemelidir. Ancak, statik bir sınıf örneği oluşturulamadığı veya türetilemediği için, hem mühürlü hem de soyut gibi davranır.
  • Statik sınıf bir class_base belirtimi (§15.2.4) içermeyecektir ve açıkça bir temel sınıf veya uygulanan arabirim listesi belirtemez. Statik bir sınıf, tür object'den örtük olarak devralır.
  • Statik sınıf yalnızca statik üyeler içermelidir (§15.3.8).

    Not: Tüm sabitler ve iç içe türler statik üye olarak sınıflandırılır. son not

  • Statik bir sınıfın , veya protectedprivate protected bildirilen erişilebilirliği olan protected internalüyeleri olmamalıdır.

Bu kısıtlamalardan herhangi birini ihlal etmek bir derleme zamanı hatasıdır.

Statik sınıfın örnek oluşturucuları yoktur. Statik bir sınıfta örnek oluşturucu bildirmek mümkün değildir ve statik sınıf için varsayılan örnek oluşturucu (§15.11.5) sağlanmaz.

Statik sınıfın üyeleri otomatik olarak statik değildir ve üye bildirimleri açıkça bir static değiştirici içermelidir (sabitler ve iç içe türler hariç). Bir sınıf statik dış sınıfın içine yerleştirildiğinde, iç içe geçmiş sınıf açıkça değiştirici içermediği sürece statik bir static sınıf değildir.

Bir sınıfın kısmi tür bildiriminin (§15.2.7) bir veya daha fazla bölümü değiştiriciyi static içerirse, sınıf statiktir. Aksi takdirde, sınıf statik değildir.

15.2.2.4.2 Statik sınıf türlerine başvuruyor

Bir namespace_or_type_name (§7.8) statik bir sınıfa başvuruda bulunabilir

  • namespace_or_type_name, formunun T namespace_or_type_name T.I veya
  • namespace_or_type-adı, formun Ttypeof_expression (typeof(T)) içindeki adıdır.

Bir primary_expression (§12.8) statik bir sınıfı referans alabilir.

  • primary_expression, formun Emember_access(E.I) içindekidir.

Başka bir bağlamda, statik bir sınıfa başvurmak bir derleme zamanı hatasıdır.

Not: Örneğin, statik sınıfın temel sınıf, bir üyenin kurucu türü (§15.3.7), genel tür bağımsız değişkeni veya tür parametresi kısıtlaması olarak kullanılması hatadır. Benzer şekilde, statik sınıf dizi türünde, yeni ifadede, atama ifadesinde, is ifadesinde, as ifadesinde, sizeof ifadede veya varsayılan değer ifadesinde kullanılamaz. son not

15.2.3 Tür parametreleri

Tür parametresi, bir türü oluşturmak için sağlanan tür bağımsız değişkeni için yer tutucuyu belirten basit bir tanımlayıcıdır. Buna karşılık, tür bağımsız değişkeni (§8.4.2), oluşturulan bir tür oluşturulduğunda tür parametresinin yerine geçecek türdür.

type_parameter_list
    : '<' decorated_type_parameter (',' decorated_type_parameter)* '>'
    ;

decorated_type_parameter
    : attributes? type_parameter
    ;

type_parameter §8.5 içinde tanımlanır.

Sınıf bildirimindeki her tür parametresi, bu sınıfın bildirim alanında (§7.3) bir ad tanımlar. Bu nedenle, o sınıfın başka bir tür parametresi veya o sınıfta bildirilen bir üye ile aynı ada sahip olamaz. Tür parametresi, türün kendisiyle aynı ada sahip olamaz.

İki kısmi genel tür bildirimi (aynı programda) aynı tam ada (tür parametresi sayısı için generic_dimension_specifier (§12.8.18) içeren) (§7.8.3) sahipse, aynı ilişkisiz genel türe katkıda bulunur. Bu türdeki iki kısmi tür bildirimi, her tür parametresi için sırasıyla aynı adı belirtecektir.

15.2.4 Sınıf temel belirtimi

15.2.4.1 Genel

Sınıf bildirimi, sınıfın doğrudan temel sınıfını ve doğrudan sınıf tarafından uygulanan arabirimleri (§19) tanımlayan bir class_base belirtimi içerebilir.

class_base
    : ':' class_type
    | ':' interface_type_list
    | ':' class_type ',' interface_type_list
    ;

interface_type_list
    : interface_type (',' interface_type)*
    ;

15.2.4.2 Temel sınıflar

bir class_type class_base dahil edildiğinde, bildirilmekte olan sınıfın doğrudan temel sınıfını belirtir. Kısmi olmayan bir sınıf bildiriminde class_base yoksa veya class_base yalnızca arabirim türlerini listelediyse, doğrudan temel sınıfın olduğu objectvarsayılır. Kısmi sınıf bildirimi bir temel sınıf belirtimi içerdiğinde, bu temel sınıf belirtimi, bu kısmi türün temel sınıf belirtimini içeren diğer tüm bölümleriyle aynı türe başvuracaktır. Kısmi sınıfın hiçbir bölümü temel sınıf belirtimi içermiyorsa, temel sınıf olur object. Bir sınıf, §15.3.4'te açıklandığı gibi üyeleri doğrudan temel sınıfından devralır.

Örnek: Aşağıdaki kodda

class A {}
class B : A {}

Sınıf A, B'in doğrudan temel sınıfı olarak belirtilir ve B, A'ten türediği söylenir. A Açıkça bir doğrudan temel sınıf belirtmediğinden, doğrudan temel sınıfı örtük olarak objectolur.

son örnek

tr-TR: Genel tür bildirimi (§15.3.9.7) içerisinde bildirilen iç içe geçmiş bir tür de dahil olmak üzere, oluşturulmuş bir sınıf türü için, genel sınıf bildiriminde bir temel sınıf belirtilmişse, oluşturulmuş türdeki her type_argument, temel sınıf bildirimindeki karşılık gelen type_parameter ile yer değiştirerek temel sınıf elde edilir.

Örnek: Genel sınıf bildirimleri göz önünde bulundurulduğunda

class B<U,V> {...}
class G<T> : B<string,T[]> {...}

oluşturulmuş türün G<int> temel sınıfı olacaktır B<string,int[]>.

son örnek

Sınıf bildiriminde belirtilen temel sınıf, oluşturulmuş bir sınıf türü (§8.4) olabilir. Temel sınıf kendi başına bir tür parametresi (§8.5) olamaz, ancak kapsamdaki tür parametrelerini içerebilir.

Örnek:

class Base<T> {}

// Valid, non-constructed class with constructed base class
class Extend1 : Base<int> {}

// Error, type parameter used as base class
class Extend2<V> : V {}

// Valid, type parameter used as type argument for base class
class Extend3<V> : Base<V> {}

son örnek

Bir sınıf türünün doğrudan taban sınıfı en az sınıf türünün kendisi kadar erişilebilir olmalıdır (§7.5.5). Örneğin, bir ortak sınıfın özel veya iç sınıftan türetilmiş olması derleme zamanı hatasıdır.

Bir sınıf türünün doğrudan temel sınıfı şu türlerden biri olmamalıdır: System.Array, System.Delegate, System.Enumveya System.ValueTypedynamic türü. Ayrıca, genel bir sınıf bildirimi doğrudan veya dolaylı temel sınıf (System.Attribute) olarak kullanılmayacaktır.

Bir sınıfın A doğrudan temel sınıf belirtiminin B anlamını belirlerken, B öğesinin doğrudan temel sınıfının geçici olarak object olduğu varsayılır ve bu da temel sınıf belirtiminin anlamının kendi kendine bağımlı olmasını engeller.

Örnek: Aşağıdakiler

class X<T>
{
    public class Y{}
}

class Z : X<Z.Y> {}

hataya neden olur çünkü temel sınıf belirtiminde X<Z.Y> doğrudan temel sınıfı Z olarak kabul edilir objectve bu nedenle (§7.8 kurallarına göre) Z üyesi Yolduğu kabul edilmez.

son örnek

Bir sınıfın temel sınıfları, doğrudan temel sınıfı ve onun temel sınıflarıdır. Başka bir deyişle, temel sınıflar kümesi doğrudan temel sınıf ilişkisinin geçişli kapanışıdır.

Örnek: Aşağıdakilerde:

class A {...}
class B<T> : A {...}
class C<T> : B<IComparable<T>> {...}
class D<T> : C<T[]> {...}

temel sınıfları D<int> , C<int[]>, B<IComparable<int[]>>ve Aşeklindedirobject.

son örnek

sınıfı objectdışında, her sınıfın tam olarak bir doğrudan temel sınıfı vardır. Sınıfın object doğrudan temel sınıfı yoktur ve diğer tüm sınıfların nihai temel sınıfıdır.

Bir sınıfın kendisine bağımlı olması derleme zamanı hatasıdır. Bu kuralın amacı doğrultusunda, bir sınıf doğrudan kendi doğrudan temel sınıfına (varsa) ve doğrudan hemen iç içe yerleştirildiği türe (varsa) bağlıdır.

Örnek: Örnek

class A : A {}

hatalıdır çünkü sınıf kendisine bağlıdır. Benzer şekilde, örnek

class A : B {}
class B : C {}
class C : A {}

Sınıflar kendilerine döngüsel olarak bağımlı oldukları için hata oluşuyor. Son olarak, örnek

class A : B.C {}
class B : A
{
    public class C {}
}

Derleme zamanı hatasıyla sonuçlanır çünkü A, B.C öğesine bağımlıdır (bu, onun doğrudan temel sınıfıdır) ve bu da B öğesine bağlıdır (hemen kapsayan sınıfı), aynı şekilde bu da A öğesine döngüsel olarak bağımlıdır.

son örnek

Sınıf, içinde iç içe geçmiş sınıflara bağımlı değildir.

Örnek: Aşağıdaki kodda

class A
{
    class B : A {}
}

B, A'e bağımlıdır (çünkü A hem doğrudan temel sınıfı hem de hemen kapsayan sınıfıdır), ancak A bağımlı değildir B'e (çünkü B ne bir temel sınıf ne de kapsayan sınıfıdır A). Bu nedenle, örnek geçerlidir.

son örnek

Korumalı bir sınıftan türetmek mümkün değildir.

Örnek: Aşağıdaki kodda

sealed class A {}
class B : A {} // Error, cannot derive from a sealed class

Mühürlü sınıf B 'den türetmeye çalıştığı için A sınıfı hata veriyor.

son örnek

15.2.4.3 Arabirim uygulamaları

class_base belirtimi, arabirim türlerinin listesini içerebilir ve bu durumda sınıfın verilen arabirim türlerini uyguladığı söylenir. Genel tür bildiriminde (§15.3.9.7) bildirilen iç içe geçmiş bir tür de dahil olmak üzere oluşturulmuş bir sınıf türü için, uygulanan her arabirim türü, verilen arabirimdeki her type_parameter için, oluşturulmuş türün ilgili type_argument değiştirilerek elde edilir.

Birden çok bölümde (§15.2.7) bildirilen bir tür için arabirim kümesi, her bölümde belirtilen arabirimlerin birleşimidir. Belirli bir arabirim her bölümde yalnızca bir kez adlandırılabilir, ancak birden çok bölüm aynı temel arabirimleri adlandırabilir. Herhangi bir arabirimin her üyesinin yalnızca bir uygulaması olacaktır.

Örnek: Aşağıdakilerde:

partial class C : IA, IB {...}
partial class C : IC {...}
partial class C : IA, IB {...}

sınıfı C için temel arabirim kümesi , IAve IBşeklindedirIC.

son örnek

Genellikle her bölüm, bu bölümde bildirilen arabirimlerin bir uygulamasını sağlar; ancak bu bir gereksinim değildir. Bir bölüm, farklı bir bölümde bildirilen bir arabirim için uygulamayı sağlayabilir.

Örnek:

partial class X
{
    int IComparable.CompareTo(object o) {...}
}

partial class X : IComparable
{
    ...
}

son örnek

Sınıf bildiriminde belirtilen temel arabirimler arabirim türleri (§8.4, §19.2) oluşturulabilir. Temel arabirim kendi başına bir tür parametresi olamaz, ancak kapsamdaki tür parametrelerini içerebilir.

Örnek: Aşağıdaki kod, bir sınıfın oluşturulan türleri nasıl uygulayabileceğini ve genişletebileceğini gösterir:

class C<U, V> {}
interface I1<V> {}
class D : C<string, int>, I1<string> {}
class E<T> : C<int, T>, I1<T> {}

son örnek

Arabirim uygulamaları §19.6'da daha ayrıntılı olarak ele alınıyor.

15.2.5 Tür parametresi kısıtlamaları

Genel tür ve yöntem bildirimleri isteğe bağlı olarak type_parameter_constraints_clauses ekleyerek tür parametresi kısıtlamalarını belirtebilir.

type_parameter_constraints_clause
    : 'where' type_parameter ':' type_parameter_constraints
    ;

type_parameter_constraints
    : primary_constraint (',' secondary_constraints)? (',' constructor_constraint)?
    | secondary_constraints (',' constructor_constraint)?
    | constructor_constraint
    ;

primary_constraint
    : class_type nullable_type_annotation?
    | 'class' nullable_type_annotation?
    | 'struct'
    | 'notnull'
    | 'unmanaged'
    ;

secondary_constraint
    : interface_type nullable_type_annotation?
    | type_parameter nullable_type_annotation?
    ;

secondary_constraints
    : secondary_constraint (',' secondary_constraint)*
    ;

constructor_constraint
    : 'new' '(' ')'
    ;

Her type_parameter_constraints_clause belirteci where, ardından bir tür parametresinin adı, ardından iki nokta üst üste ve bu tür parametresi için kısıtlamaların listesi içerir. Her tür parametresi için en fazla bir where yan tümce olabilir ve where yan tümceler herhangi bir sırada listelenebilir. Bir özellik erişimcisindeki get ve set belirteçleri gibi, where belirteci de anahtar sözcük değildir.

Yan where tümcesinde verilen kısıtlamaların listesi şu sırayla aşağıdaki bileşenlerden herhangi birini içerebilir: tek bir birincil kısıtlama, bir veya daha fazla ikincil kısıtlama ve oluşturucu kısıtlaması. new()

Birincil kısıtlama bir sınıf türü, referans türü kısıtlaması, değer türü kısıtlaması, class veya yönetilmeyen tür kısıtlaması olabilir. Sınıf türü ve başvuru türü kısıtlaması nullable_type_annotation içerebilir.

Bir ikincil kısıtlama, isteğe bağlı olarak bir nullable_type_annotation ile takip edilen bir interface_type veya type_parameter olabilir. nullable_type_annotation varlığı, tür bağımsız değişkeninin kısıtlamayı karşılayan null atanamaz bir başvuru türüne karşılık gelen null atanabilir başvuru türü olmasına izin verildiğini gösterir.

Başvuru türü kısıtlaması, tür parametresi için kullanılan tür bağımsız değişkeninin bir başvuru türü olacağını belirtir. Başvuru türü olarak bilinen tüm sınıf türleri, arabirim türleri, temsilci türleri, dizi türleri ve tür parametreleri (aşağıda tanımlandığı gibi) bu kısıtlamayı karşılar.

Sınıf türü, başvuru türü kısıtlaması ve ikincil kısıtlamalar nullable tür açıklamasını içerebilir. Tür parametresinde bu açıklamanın mevcut olup olmaması, tür bağımsız değişkeninin null atanabilirlik beklentilerini belirtir.

  • Kısıtlama, null atanabilir tür açıklamasını içermiyorsa, tür parametresinin null atanamaz bir başvuru türü olması beklenir. Derleyici, tür bağımsız değişkeni null olabilen bir başvuru türüyse uyarı verebilir.
  • Kısıtlama, null atanabilir tür ek açıklamasını içeriyorsa, bu kısıtlama hem null atanamaz referans türü hem de null atanabilir referans türü tarafından karşılanır.

Tür bağımsız değişkeninin null atanabilirliği, tür parametresinin null atanabilirliği ile eşleşmek zorunda değildir. Eğer tür parametresinin null atanabilirliği, tür argümanının null atanabilirliği ile eşleşmezse, derleyici bir uyarı verebilir.

Not: Tür bağımsız değişkeninin null atanabilir bir başvuru türü olduğunu belirtmek için, null atanabilir tür ek açıklamasını kısıtlama (veya T : classT : BaseClass) olarak eklemeyin, ancak tür bağımsız değişkeni için karşılık gelen null atanabilir başvuru türünü belirtmek için genel bildirimin tamamında kullanın T? . son not

null atanabilir tür ek açıklaması, ?yalnızca değer türü kısıtlaması olan bir tür parametresinde, nullable_type_annotation olmadan başvuru türü kısıtlaması veya nullable_type_annotation olmadan bir sınıf türü kısıtlaması üzerinde kullanılabilir.

Örnek: Aşağıdaki örneklerde, bir tür bağımsız değişkeninin null atanabilirliğinin kendi tür parametresinin bildiriminin null atanabilirliğini nasıl etkilediği gösterilmektedir:

public class C
{
}

public static class  Extensions
{
    public static void M<T>(this T? arg) where T : notnull
    {

    }
}

public class Test
{
    public void M()
    {
        C? mightBeNull = new C();
        C notNull = new C();

        int number = 5;
        int? missing = null;

        mightBeNull.M(); // arg is C?
        notNull.M(); //  arg is C?
        number.M(); // arg is int?
        missing.M(); // arg is int?
    }
}

Tür bağımsız değişkeni null atanamaz bir tür olduğunda, ? tür ek açıklaması parametrenin karşılık gelen null atanabilir tür olduğunu gösterir. Tür bağımsız değişkeni zaten null atanabilir bir referans türü olduğunda, parametre aynı null atanabilir türdür.

son örnek

Null değil kısıtlaması, tür parametresi için kullanılan tür bağımsız değişkeninin null atanamaz bir değer türü veya null atanamayan başvuru türü olması gerektiğini belirtir. Null atanamayan bir değer türü veya null atanamayan bir başvuru türü olmayan bir tür bağımsız değişkenine izin verilir, ancak derleyici bir tanılama uyarısı üretebilir.

notnull bir anahtar sözcük olmadığından, primary_constraint not null kısıtlaması, her zaman class_typeile sintaks açısından belirsizlik oluşturur. Uyumluluk nedenleriyle, adın bir ad araması (§12.8.4) notnull başarılı olursa, olarak değerlendirilir class_type. Aksi takdirde null değil kısıtlaması olarak kabul edilir.

Örnek: Aşağıdaki sınıf, farklı kısıtlamalara karşı çeşitli tür bağımsız değişkenlerinin kullanımını gösterir ve derleyici tarafından verilebilir uyarıları gösterir.

#nullable enable
public class C { }
public class A<T> where T : notnull { }
public class B1<T> where T : C { }
public class B2<T> where T : C? { }
class Test
{
    static void M()
    {
        // nonnull constraint allows nonnullable struct type argument
        A<int> x1;
        // possible warning: nonnull constraint prohibits nullable struct type argument
        A<int?> x2;
        // nonnull constraint allows nonnullable class type argument
        A<C> x3;
        // possible warning: nonnull constraint prohibits nullable class type argument
        A<C?> x4;
        // nonnullable base class requirement allows nonnullable class type argument
        B1<C> x5;
        // possible warning: nonnullable base class requirement prohibits nullable
        // class type argument
        B1<C?> x6;
        // nullable base class requirement allows nonnullable class type argument
        B2<C> x7;
        // nullable base class requirement allows nullable class type argument
        B2<C?> x8;
    }
}

Değer türü kısıtlaması, tür parametresi için kullanılan tür bağımsız değişkeninin null atanamayan bir değer türü olacağını belirtir. Değer türü kısıtlaması olan tüm null atanamayan yapı türleri, sabit listesi türleri ve tür parametreleri bu kısıtlamayı karşılar. Değer türü olarak sınıflandırılsa da, null atanabilir bir değer türünün (§8.3.12) değer türü kısıtlamasını karşılamadığını unutmayın. Değer türü kısıtlamasına sahip bir tür parametresi, constructor_constraint içermeyecektir; ancak, constructor_constraint bulunan başka bir tür parametresi için tür bağımsız değişkeni olarak kullanılabilir.

Not: türü System.Nullable<T> için Tnull atanamaz değer türü kısıtlamasını belirtir. Bu nedenle, T?? ve Nullable<Nullable<T>> formlarında özyinelemeli olarak oluşturulmuş türler yasaktır. son not

Yönetilmeyen tür kısıtlaması, tür parametresi için kullanılan tür bağımsız değişkeninin null atanamayan yönetilmeyen bir tür (§8.8) olacağını belirtir.

Çünkü unmanaged bir anahtar sözcük değildir, primary_constraint yönetilmeyen kısıtlama her zaman class_type ile kesin olarak belirsizdir. Uyumluluk nedeniyle, adın bir ad araması (§12.8.4) unmanaged başarılı olursa, olarak değerlendirilir class_type. Aksi takdirde yönetilmeyen kısıtlama olarak değerlendirilir.

İşaretçi türlerinin hiçbir zaman tür bağımsız değişkeni olmasına izin verilmez ve yönetilmeyen türler olmasına rağmen, yönetilmeyen bile olsa hiçbir tür kısıtlamasını karşılamaz.

Bir kısıtlama bir sınıf türü, arabirim türü veya tür parametresiyse, bu tür, bu tür parametresi için kullanılan her tür bağımsız değişkeninin destekleyebilecekleri en küçük bir "temel tür" belirtir. Oluşturulmuş bir tür veya genel yöntem her kullanıldığında, tür bağımsız değişkeni derleme zamanında tür parametresindeki kısıtlamalarla denetlenir. Sağlanan tür bağımsız değişkeni , §8.4.5'te açıklanan koşulları karşılaması gerekir.

class_type kısıtlaması aşağıdaki kuralları karşılamalı:

  • Türü bir sınıf türü olacaktır.
  • Türü sealed olmamalıdır.
  • Türü şu türlerden biri olmamalıdır: System.Array veya System.ValueType.
  • Türü object olmamalıdır.
  • Belirli bir tür parametresi için en fazla bir kısıtlama bir sınıf türü olabilir.

interface_type kısıtlaması olarak belirtilen bir tür aşağıdaki kuralları karşılamalı:

  • Tür bir arabirim türü olmalıdır.
  • Belirli where bir yan tümcede bir tür birden çok kez belirtilmez.

Her iki durumda da kısıtlama, ilişkilendirilmiş türün veya yöntem bildiriminin tür parametrelerinden herhangi birini, yapılı bir türün parçası olarak içerebilir ve türün bildirildiğini içerebilir.

Tür parametresi kısıtlaması olarak belirtilen herhangi bir sınıf veya arabirim türü, bildirilen genel tür veya yöntem kadar en az erişilebilir (§7.5.5) olmalıdır.

type_parameter kısıtlaması olarak belirtilen bir tür aşağıdaki kuralları karşılamalı:

  • Tür bir tür parametresi olmalıdır.
  • Belirli where bir yan tümcede bir tür birden çok kez belirtilmez.

Ayrıca, bağımlılık türü parametrelerinin bağımlılık grafiğinde hiçbir döngü olmayacaktır; burada bağımlılık, aşağıdakiler tarafından tanımlanan geçişli bir ilişkidir:

  • Tür parametresi, türTS parametresi S için bir kısıtlama olarak kullanılıyorsa, buna bağlıdır.T
  • Tür parametresi bir tür parametresine S ve tür parametresine TTU bağımlıysa, o zaman SbağlıdırU.

Bu ilişki göz önüne alındığında, bir tür parametresinin kendisine bağımlı olması (doğrudan veya dolaylı) için derleme zamanı hatasıdır.

Tüm kısıtlamalar, bağımlı tür parametreleri arasında tutarlı olmalıdır. Tür parametresi tür parametresine ST bağlıysa:

  • T değer türü kısıtlaması olmamalıdır. Aksi takdirde, T etkili bir şekilde mühürlendiğinden S , ile aynı türde Tolmaya zorlanır ve iki tür parametre gereksinimi ortadan kaldırılır.
  • Değer türü kısıtlaması S varsa T class_type kısıtlaması olmamalıdır.
  • Eğer S bir class_type kısıtlamasına sahipse ve A bir T kısıtlamasına sahipse, o zaman 'dan B'ye kimlik dönüşümü veya örtük başvuru dönüşümü ya da A'den B'ya örtük başvuru dönüşümü olacaktır.
  • Ayrıca tür parametresine S bağlıysa ve U class_typeUkısıtlaması varsa A ve T class_type kısıtlaması B varsa, 'den bir kimlik dönüştürmesi veya örtük başvuru dönüştürmesi veya 'den AB 'a BAörtük başvuru dönüştürmesi olacaktır.

Değer türü kısıtlamasına sahip olmak ve S başvuru türü kısıtlamasına sahip olmak için T geçerlidir. Bu, etkin bir şekilde T, System.Object, System.ValueType, System.Enum türleri ve herhangi bir arabirim türü ile sınırlar.

Tür parametresinin where yan tümcesi bir oluşturucu kısıtlaması içeriyorsa (biçimine new()sahip), işlecini new kullanarak türün örneklerini (§12.8.17.2) oluşturabilirsiniz. Oluşturucu kısıtlaması olan bir tür parametresi için kullanılan herhangi bir tür bağımsız değişkeni, bir değer türü, genel, parametresiz bir oluşturucuya sahip soyut olmayan bir sınıf veya değer türü kısıtlaması ya da oluşturucu kısıtlaması olan bir tür parametresi olmalıdır.

primary_constraint veya constructor_constraint sahip type_parameter_constraints derleme zamanı hatasıdır.struct

Örnek: Kısıtlama örnekleri aşağıda verilmiştir:

interface IPrintable
{
    void Print();
}

interface IComparable<T>
{
    int CompareTo(T value);
}

interface IKeyProvider<T>
{
    T GetKey();
}

class Printer<T> where T : IPrintable {...}
class SortedList<T> where T : IComparable<T> {...}

class Dictionary<K,V>
    where K : IComparable<K>
    where V : IPrintable, IKeyProvider<K>, new()
{
    ...
}

Aşağıdaki örnek, tür parametrelerinin bağımlılık grafiğinde döngüselliğe neden olduğundan hatayla karşılanır:

class Circular<S,T>
    where S: T
    where T: S // Error, circularity in dependency graph
{
    ...
}

Aşağıdaki örneklerde ek geçersiz durumlar gösterilmektedir:

class Sealed<S,T>
    where S : T
    where T : struct // Error, `T` is sealed
{
    ...
}

class A {...}
class B {...}

class Incompat<S,T>
    where S : A, T
    where T : B // Error, incompatible class-type constraints
{
    ...
}

class StructWithClass<S,T,U>
    where S : struct, T
    where T : U
    where U : A // Error, A incompatible with struct
{
    ...
}

son örnek

Bir türün dinamik silinmesi aşağıdaki gibi oluşturulur:

  • İç içe bir türse COuter.InnerCₓ, iç içe bir türdür.Outerₓ.Innerₓ
  • Tür CCₓ, tür bağımsız değişkenleri G<A¹, ..., Aⁿ> olan yapılı tür A¹, ..., Aⁿ ise, Cₓ yapılı tür G<A¹ₓ, ..., Aⁿₓ> olur.
  • Eğer C bir dizi türüyse E[], o zaman Cₓ dizi türü Eₓ[] olur.
  • Eğer C dinamikse, Cₓobject olur.
  • Aksi takdirde, Cₓ şeklindedir C.

Tür aşağıdaki gibi tanımlanır:

Şöyle R bir tür kümesi olsun:

  • Bunun bir tür parametresi olan her kısıtlaması T için geçerli R temel sınıfını içerir.
  • Yapı türünde olan her T kısıtlaması için, RSystem.ValueType içerir.
  • Her bir T kısıtlaması numaralandırma türüyse, R öğesi System.Enum içerir.
  • T'ün bir temsilci türü olan her kısıtlaması için, R onun dinamik silmesini içerir.
  • T bir dizi türü olan her kısıtlama için R, System.Array içerir.
  • Bunun bir sınıf türü olan her kısıtlaması T için dinamik R silmeyi içerir.

Ardından

  • Değer türü kısıtlamasına sahipse T , geçerli temel sınıfı olur System.ValueType.
  • Aksi takdirde, R boşsa, etkin temel sınıf object olur.
  • Aksi takdirde, etkin taban sınıfı T kümesinin en çok kapsamış türüdür (R). Kümenin kapsanan bir türü yoksa, etkin temel sınıfı T olur object. Tutarlılık kuralları, en çok kapsamış türün mevcut olmasını sağlar.

tür parametresi, kısıtlamaları temel yöntemden devralınan bir yöntem türü parametresiyse, geçerli temel sınıf tür değiştirmesinden sonra hesaplanır.

Bu kurallar, etkili temel sınıfın her zaman bir class_type olmasını sağlar.

Tür parametresinin etkili arayüz seti aşağıdaki gibi tanımlanır:

  • Eğer T'ın ikincil kısıtlamaları yoksa, etkin arabirim kümesi boş olur.
  • ** Tinterface_type kısıtlamaları varsa ancak type_parameter kısıtlamaları yoksa, geçerli arabirim kümesi interface_type kısıtlamalarının dinamik silinmiş halleri kümesidir.
  • T interface_type kısıtlamaları yoksa fakat type_parameter kısıtlamaları varsa, etkin arabirim kümesi, type_parameter kısıtlamalarının etkili arabirim kümelerinin birleşimidir.
  • Hem T kısıtlamaları hem type_parameter kısıtlamaları varsa, etkin arabirim kümesi, interface_type kısıtlamalarının dinamik erasures kümesinin ve type_parameter kısıtlamalarının etkin arabirim kümelerinin birleşimidir.

Tür parametresi, referans tür kısıtlamasına sahip olduğunda veya etkili temel sınıfı veya object olmadığında bir referans türü olarak bilinir. Tür parametresi, başvuru türü olarak biliniyorsa ve null atanamaz başvuru türü kısıtlamasına sahipse null atanamaz başvuru türü olarak bilinir.

Kısıtlanmış tür parametre türünün değerleri, kısıtlamalar tarafından ima edilen örnek üyelerine erişmek için kullanılabilir.

Örnek: Aşağıdakilerde:

interface IPrintable
{
    void Print();
}

class Printer<T> where T : IPrintable
{
    void PrintOne(T x) => x.Print();
}

IPrintable yöntemleri doğrudan x üzerinde çağrılabilir çünkü T, daima IPrintable'ü uygulamakla kısıtlanmıştır.

son örnek

Kısmi genel tür bildirimi kısıtlamalar içerdiğinde, kısıtlamalar kısıtlamaları içeren diğer tüm bölümleri kabul eder. Özellikle, kısıtlamalar içeren her parça aynı tür parametreleri kümesi için kısıtlamalara sahip olacaktır ve her tür parametresi için birincil, ikincil ve oluşturucu kısıtlamaları kümeleri eşdeğer olacaktır. Aynı üyeleri içeren iki kısıtlama kümesi eşdeğerdir. Kısmi genel türün hiçbir parçası tür parametresi kısıtlamalarını belirtmezse, tür parametreleri kısıtlanmamış olarak kabul edilir.

Örnek:

partial class Map<K,V>
    where K : IComparable<K>
    where V : IKeyProvider<K>, new()
{
    ...
}

partial class Map<K,V>
    where V : IKeyProvider<K>, new()
    where K : IComparable<K>
{
    ...
}

partial class Map<K,V>
{
    ...
}

doğru çünkü kısıtlamaları içeren parçalar (ilk ikisi) sırasıyla aynı tür parametreleri kümesi için aynı birincil, ikincil ve oluşturucu kısıtlamalarını etkili bir şekilde belirtir.

son örnek

15.2.6 Sınıf gövdesi

Bir sınıfın class_body , bu sınıfın üyelerini tanımlar.

class_body
    : '{' class_member_declaration* '}'
    ;

15.2.7 Kısmi tür bildirimleri

Değiştirici partial , birden çok bölümde bir sınıf, yapı veya arabirim türü tanımlarken kullanılır. Değiştirici partial bağlamsal bir anahtar sözcüktür (§6.4.4) ve , classve structanahtar sözcüklerinden interfacehemen önce özel bir anlamı vardır. (Kısmi bir tür kısmi yöntem bildirimleri içerebilir (§15.6.9).

Kısmi tür bildiriminin her bölümü bir partial değiştirici içermelidir ve diğer parçalarda olduğu gibi aynı ad alanında veya türü içeren şekilde bildirilmelidir. Değiştirici, partial tür bildiriminin ek bölümlerinin başka bir yerde mevcut olabileceğini gösterir, ancak bu tür ek bölümlerin varlığı bir gereksinim değildir; değiştiriciyi içerecek partial tek tür bildirimi için geçerlidir. Temel sınıfı veya uygulanan arabirimleri dahil etmek için kısmi türün yalnızca bir bildirimi için geçerlidir. Ancak, bir temel sınıfın veya uygulanan arabirimlerin tüm bildirimleri, belirtilen tür bağımsız değişkenlerinin null olma durumu da dahil olmak üzere, eşleşmelidir.

Kısmi bir türün tüm bölümleri, derleme zamanında birleştirilecek şekilde birlikte derlenmelidir. Kısmi türler özel olarak önceden derlenmiş türlerin genişletilmesine izin vermez.

İç içe türler, değiştirici kullanılarak partial birden çok bölümde bildirilebilir. Genellikle, içeren tür de kullanılarak partial bildirilir ve iç içe türün her bölümü, içeren türün farklı bir bölümünde bildirilir.

Örnek: Aşağıdaki kısmi sınıf, farklı derleme birimlerinde bulunan iki bölümde uygulanır. İlk bölüm, bir veritabanı eşleme aracı tarafından makine tarafından oluşturulurken, ikinci bölüm manuel olarak yazılmıştır.

public partial class Customer
{
    private int id;
    private string name;
    private string address;
    private List<Order> orders;

    public Customer()
    {
        ...
    }
}

// File: Customer2.cs
public partial class Customer
{
    public void SubmitOrder(Order orderSubmitted) => orders.Add(orderSubmitted);

    public bool HasOutstandingOrders() => orders.Count > 0;
}

Yukarıdaki iki bölüm birlikte derlendiğinde, sonuçta elde edilen kod sınıfı tek bir birim olarak yazılmış gibi davranır, aşağıdaki gibi:

public class Customer
{
    private int id;
    private string name;
    private string address;
    private List<Order> orders;

    public Customer()
    {
        ...
    }

    public void SubmitOrder(Order orderSubmitted) => orders.Add(orderSubmitted);

    public bool HasOutstandingOrders() => orders.Count > 0;
}

son örnek

Kısmi tür bildiriminin farklı bölümlerinin tür veya tür parametrelerinde belirtilen özniteliklerin işlenmesi §23.3'te ele alınıyor.

15.3 Sınıf üyeleri

15.3.1 Genel

Bir sınıfın üyeleri, class_member_declarationtarafından tanıtılan üyelerden ve doğrudan temel sınıftan devralınan üyelerden oluşur.

class_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | finalizer_declaration
    | static_constructor_declaration
    | type_declaration
    ;

Sınıfın üyeleri aşağıdaki kategorilere ayrılır:

  • Sınıfıyla ilişkili sabit değerleri temsil eden sabitler (§15.4).
  • Sınıfın değişkenleri olan alanlar (§15.5).
  • Sınıf (§15.6) tarafından gerçekleştirilebilecek hesaplamaları ve eylemleri uygulayan yöntemler.
  • Adlandırılmış özellikleri ve bu özellikleri okuma ve yazma ile ilişkili eylemleri tanımlayan özellikler (§15.7).
  • Sınıf (§15.8) tarafından oluşturulabilecek bildirimleri tanımlayan olaylar.
  • Dizin oluşturucular, sınıfın örneklerinin dizilerle (§15.9) aynı şekilde dizine alınmasına izin verir.
  • Sınıfın örneklerine uygulanabilecek ifade işleçlerini tanımlayan işleçler (§15.10).
  • Sınıfın örneklerini başlatmak için gereken eylemleri uygulayan örnek oluşturucuları (§15.11)
  • Sınıfın örnekleri kalıcı olarak atılmadan önce gerçekleştirilecek eylemleri yerine getiren sonlandırıcılar (§15.13).
  • Sınıfın kendisini başlatmak için gerekli eylemleri uygulayan statik oluşturucular (§15.12).
  • Sınıfın yerel türlerini temsil eden türler (§14.7).

class_declaration yeni bir bildirim alanı (§7.3) oluşturur ve class_declaration tarafından doğrudan içerilen type_parameterlar ve class_member_declarationlar bu bildirim alanında yeni üyeler tanımlar. Aşağıdaki kurallar class_member_declarationiçin geçerlidir:

  • Örnek oluşturucular, sonlandırıcılar ve statik oluşturucular, hemen kapsayan sınıfla aynı ada sahip olmalıdır. Diğer tüm üyeler, hemen kapsayan sınıfın adından farklı adlara sahip olmalıdır.

  • Sınıf bildiriminin type_parameter_list tür parametresinin adı, aynı type_parameter_list diğer tüm tür parametrelerinin adlarından farklı olacaktır ve sınıfın adından ve sınıfın tüm üyelerinin adlarından farklı olacaktır.

  • Bir türün adı, aynı sınıfta bildirilen tür olmayan tüm üyelerin adlarından farklı olmalıdır. İki veya daha fazla tür bildirimi aynı tam adı paylaşıyorsa, bu bildirimlerin her birinde partial değiştiricisi (§15.2.7) bulunmalı ve bu bildirimler tek bir tür belirlemek için birleştirilmelidir.

Not: Tür bildiriminin tam adı tür parametrelerinin sayısını kodladığından, farklı sayıda tür parametresine sahip oldukları sürece iki farklı tür aynı adı paylaşabilir. son not

  • Sabit, alan, özellik veya olayın adı, aynı sınıfta bildirilen diğer tüm üyelerin adlarından farklı olmalıdır.

  • Bir yöntemin adı, aynı sınıfta bildirilen diğer tüm yöntem dışı yöntemlerin adlarından farklı olmalıdır. Ayrıca, bir yöntemin imzası (§7.6) aynı sınıfta bildirilen diğer tüm yöntemlerin imzalarından farklı olacaktır ve aynı sınıfta bildirilen iki yöntem yalnızca , inve outile reffarklı imzalara sahip olmayacaktır.

  • Bir örnek oluşturucusunun imzası, aynı sınıfta bildirilen diğer tüm örnek oluşturucularının imzalarından farklı olmalı ve aynı sınıfta bildirilen iki oluşturucunun yalnızca ref ve outile farklı imzaları olmayacaktır.

  • Bir dizin oluşturucunun imzası, aynı sınıfta bildirilen diğer tüm dizin oluşturucuların imzalarından farklı olmalıdır.

  • Bir işlecin imzası, aynı sınıfta bildirilen diğer tüm işleçlerin imzalarından farklı olmalıdır.

Bir sınıfın devralınan üyeleri (§15.3.4), sınıfın bildirim alanının bir parçası değildir.

Not: Bu nedenle, türetilmiş bir sınıfın devralınan üyeyle aynı ada veya imzaya sahip bir üyeyi bildirmesine izin verilir (bu da devralınan üyeyi gizler). son not

Birden çok bölümde bildirilen bir türün üyeleri kümesi (§15.2.7), her bölümde bildirilen üyelerin birleşimidir. Tür bildiriminin tüm bölümlerinin gövdeleri aynı bildirim alanını (§7.3) paylaşır ve her üyenin (§7.7) kapsamı tüm parçaların gövdelerine kadar uzanır. Herhangi bir üyenin erişilebilirlik etki alanı her zaman kapsayan türün tüm bölümlerini içerir; bir bölümde bildirilen özel üyeye başka bir bölümden serbestçe erişilebilir. Türün birden fazla bölümünde aynı üyeyi bildirmek bir derleme zamanı hatasıdır, ancak partial değiştiricisine sahip olan bir üye için bu geçerli değildir.

Örnek:

partial class A
{
    int x;                   // Error, cannot declare x more than once
    partial void M();        // Ok, defining partial method declaration

    partial class Inner      // Ok, Inner is a partial type
    {
        int y;
    }
}

partial class A
{
    int x;                   // Error, cannot declare x more than once
    partial void M() { }     // Ok, implementing partial method declaration

    partial class Inner      // Ok, Inner is a partial type
    {
        int z;
    }
}

son örnek

Alan başlatma sırası C# kodu içinde önemli olabilir ve §15.5.6.1'de tanımlandığı gibi bazı garantiler sağlanır. Aksi takdirde, bir tür içindeki üyelerin sıralanması nadiren önemlidir, ancak diğer diller ve ortamlar arasında geçiş yaparken önemli olabilir. Bu gibi durumlarda, birden çok bölümde bildirilen bir tür içindeki üyelerin sıralaması tanımlanmamıştır.

15.3.2 Örnek türü

Her sınıf bildirimi ilişkili bir örnek türüne sahiptir. Genel bir sınıf bildirimi için örnek türü, tür bildiriminden oluşturulmuş bir tür (§8.4) oluşturularak oluşturulur ve sağlanan tür bağımsız değişkenlerinin her biri ilgili tür parametresi olur. Örnek türü tür parametrelerini kullandığından, yalnızca tür parametrelerinin kapsam dahilinde olduğu yerlerde kullanılabilir; yani sınıf bildiriminin içinde. Örnek tipi, sınıf bildiriminde içinde yazılan kodun this türüdür. Genel olmayan sınıflar için örnek türü yalnızca bildirilen sınıftır.

Örnek: Aşağıda, örnek türleriyle birlikte birkaç sınıf bildirimi gösterilmektedir:

class A<T>             // instance type: A<T>
{
    class B {}         // instance type: A<T>.B
    class C<U> {}      // instance type: A<T>.C<U>
}
class D {}             // instance type: D

son örnek

15.3.3 Yapılı türlerin üyeleri

Çalışılan türün devralınmayan üyeleri, üye bildirimindeki her type_parameter yerine, çalışılan türün ilgili type_argument konularak elde edilir. Değiştirme işlemi, tür bildirimlerinin anlamsal anlamını temel alır ve yalnızca metinsel değiştirme değildir.

Örnek: Genel sınıf bildirimi göz önünde bulundurulduğunda

class Gen<T,U>
{
    public T[,] a;
    public void G(int i, T t, Gen<U,T> gt) {...}
    public U Prop { get {...} set {...} }
    public int H(double d) {...}
}

oluşturulan tür Gen<int[],IComparable<string>> aşağıdaki üyelere sahiptir:

public int[,][] a;
public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {...}
public IComparable<string> Prop { get {...} set {...} }
public int H(double d) {...}

Genel sınıf bildirimindeki üye a tür olarak “Gen iki boyutlu bir dizi”dir, bu nedenle yukarıdaki yapılandırılmış türdeki üye T türü “a'nin tek boyutlu dizisinin iki boyutlu dizisi”, yani int olarak tanımlanır.

son örnek

Örnek işlevi üyelerinde, this türü, içeren bildirimin örneğin türüdür (§15.3.2).

Genel sınıfın tüm üyeleri, doğrudan veya oluşturulmuş bir türün parçası olarak herhangi bir kapsayan sınıftan tür parametrelerini kullanabilir. Çalışma zamanında belirli bir kapalı yapılandırılmış tür (§8.4.3) kullanıldığında, tür parametresinin her kullanımı, yapılandırılmış türe sağlanan tür bağımsız değişkeniyle değiştirilir.

Örnek:

class C<V>
{
    public V f1;
    public C<V> f2;

    public C(V x)
    {
        this.f1 = x;
        this.f2 = this;
    }
}

class Application
{
    static void Main()
    {
        C<int> x1 = new C<int>(1);
        Console.WriteLine(x1.f1);              // Prints 1

        C<double> x2 = new C<double>(3.1415);
        Console.WriteLine(x2.f1);              // Prints 3.1415
    }
}

son örnek

15.3.4 Devralma

Bir sınıf, doğrudan temel sınıfının üyelerini devralır. Devralma, bir sınıfın temel sınıfın örnek oluşturucuları, sonlandırıcıları ve statik oluşturucuları dışında doğrudan temel sınıfının tüm üyelerini örtük olarak içerdiği anlamına gelir. Devralmayla ilgili bazı önemli yönler şunlardır:

  • Devralma geçişli. öğesinden türetilirse ve C öğesinden BB türetilirseA, içinde bildirilen üyelerin yanı sıra içinde CBbildirilen üyeleri de devralır.A

  • Türetilmiş bir sınıf , doğrudan temel sınıfını genişletir . Türetilmiş bir sınıf devralınanlara yeni üyeler ekleyebilir, ancak devralınan üyenin tanımını kaldıramaz.

  • Örnek oluşturucular, sonlandırıcılar ve statik oluşturucular devralınır, ancak diğer tüm üyeler, bildirilen erişilebilirliklerinden bağımsız olarak (§7.5) vardır. Ancak, bildirilen erişilebilirliklerine bağlı olarak, devralınan üyeler türetilmiş bir sınıfta erişilebilir olmayabilir.

  • Türetilmiş bir sınıf, aynı ada veya imzaya sahip yeni üyeler bildirerek devralınan üyeleri gizleyebilir (§7.7.2.3). Ancak, devralınan bir üyenin gizlenmesi bu üyeyi kaldırmaz; yalnızca bu üyeye doğrudan türetilmiş sınıf aracılığıyla erişilemez hale gelir.

  • Bir sınıfın örneği, sınıfında ve temel sınıflarında bildirilen tüm örnek alanlarının bir kümesini içerir ve türetilmiş bir sınıf türünden temel sınıf türlerinden herhangi birine örtük bir dönüştürme (§10.2.8) vardır. Bu nedenle, türetilmiş bazı sınıfın bir örneğine yapılan başvuru, temel sınıflarından herhangi birinin örneğine başvuru olarak kabul edilebilir.

  • Bir sınıf sanal yöntemleri, özellikleri, dizin oluşturucuları ve olayları bildirebilir ve türetilmiş sınıflar bu işlev üyelerinin uygulanmasını geçersiz kılabilir. Bu, sınıfların bir işlev üyesi çağırması tarafından gerçekleştirilen eylemlerde, bu işlev üyesinin çağrıldığı örneğin çalışma zamanı türüne bağlı olarak değiştiği polimorfik davranışlar sergilemesini sağlar.

Oluşturulmuş bir sınıf türünün devralınan üyeleri, base_class_specification içindeki karşılık gelen tür parametrelerinin her birinin yerine, oluşturulmuş türün tür bağımsız değişkenlerinin konulmasıyla bulunan doğrudan temel sınıf türünün (§15.2.4.2) üyeleridir. Bu üyeler de üye bildirimindeki her type_parameter base_class_specification karşılık gelen type_argumentdeğiştirilerek dönüştürülür.

Örnek:

class B<U>
{
    public U F(long index) {...}
}

class D<T> : B<T[]>
{
    public T G(string s) {...}
}

Yukarıdaki kodda, tür bağımsız değişkeni D<int>'in tür parametresi int yerine kullanılmasıyla oluşturulan G(string s) tipi, devralınmayan bir açık üyeye intT sahiptir. D<int> ayrıca sınıf bildiriminden Bdevralınan bir üyesi vardır. Bu devralınan üye, temel sınıf belirtiminde B<int[]> yerine D<int> konularak int'ün temel sınıf türü T olarak belirlenmesiyle belirlenir. Ardından, türüne bağımsız değişken Bint[]U olarak, öğesinde public U F(long index)yerine devralınan üyeyi public int[] F(long index)verir.

son örnek

15.3.5 Yeni değiştirici

Bir class_member_declaration'ın, devralınan bir üye ile aynı adı veya imzayı taşıyan bir üye bildirmesine izin verilir. Bu durum oluştuğunda, türetilmiş sınıf üyesinin temel sınıf üyesini gizley dediği söylenir. Üyenin devralınan bir üyeyi ne zaman gizlediğinin kesin belirtimi için bkz . §7.7.2.3 .

Devralınan bir üye M, erişilebilir durumda ise ve zaten gizleyen başka bir devralınan erişilebilir N üyesi yoksa kullanılabilirM. Devralınan bir üyenin örtük olarak gizlenmesi hata olarak kabul edilmez, ancak türetilmiş sınıf üyesinin bildiriminde türetilen üyenin temel üyeyi gizlemeye yönelik olduğunu açıkça belirtmek üzere bir new değiştirici içermediği sürece derleyici bir uyarı yayınlar. İç içe türün kısmi bildiriminin (§15.2.7) bir veya daha fazla bölümü new değiştiricisini içeriyorsa, iç içe tür mevcut devralınan bir üyeyi gizlediğinde uyarı verilmez.

new Bir değiştirici, kullanılabilir devralınan bir üyeyi gizlemeyen bir bildirime dahil edilirse, bu etkiye yönelik bir uyarı verilir.

15.3.6 Erişim değiştiricileri

bir class_member_declaration izin verilen bildirilen erişilebilirlik türlerinden herhangi birine (§7.5.2) sahip olabilir: public, protected internal, protected, private protected, , internalveya private. protected internal ve private protected birleşimleri dışında, birden fazla erişim değiştirici belirtmek bir derleme zamanı hatasıdır. Bir class_member_declaration herhangi bir erişim değiştiricisi içermediğinde, private varsayılır.

15.3.7 Kurucu türleri

Bir üyenin bildiriminde kullanılan türler, bu üyenin kurucu türüolarak adlandırılır. Olası bileşenler sabit, alan, özellik, olay veya dizin oluşturucunun türü, yöntem veya işlecin dönüş türü ve bir yöntemin, dizin oluşturucunun, işlecin veya örnek oluşturucunun parametre türleridir. Bir üyenin kurucu türleri en az o üyenin kendisi kadar erişilebilir olmalıdır (§7.5.5).

15.3.8 Statik ve örnek üyeleri

Sınıfın üyeleri statik üyeveya örnek üyesidir.

Not: Genel olarak ifade etmek gerekirse, statik üyelerin sınıflara ve örnek üyelerine nesnelere (sınıf örnekleri) ait olduğunu düşünmek yararlıdır. son not

Bir alan, yöntem, özellik, olay, işleç veya oluşturucu bildirimi bir static değiştirici içerdiğinde, statik bir üye bildirir. Buna ek olarak, sabit veya tür bildirimi örtük olarak bir statik üye bildirir. Statik üyeler aşağıdaki özelliklere sahiptir:

  • Statik bir üye M bir üye_erişimi (Bölüm §12.8.7) biçiminde referans alındığında, bir türü belirtmeli ve bu türün bir üyesi E.M bulunmalıdır. E ifadesinin bir örneği belirtmesi derleme zamanı hatasıdır.
  • Genel olmayan bir sınıftaki statik alan tam olarak bir depolama konumu tanımlar. Genel olmayan bir sınıfın kaç örneği oluşturulursa oluşturulsın, statik bir alanın yalnızca bir kopyası vardır. Her ayrı kapalı yapılı türün (§8.4.3) kapalı yapılı türün örnek sayısından bağımsız olarak kendi statik alanları vardır.
  • Statik işlev üyesi (yöntem, özellik, olay, işleç veya oluşturucu) belirli bir örnekte çalışmaz ve böyle bir işlev üyesinde buna başvurmak derleme zamanı hatasıdır.

Bir alan, yöntem, özellik, olay, indeksleyici, oluşturucu veya sonlandırıcı bildirimi statik değiştirici içermediğinde, bir örnek üye bildirir. (Örnek üyesi bazen statik olmayan üye olarak adlandırılır.) Örnek üyeleri aşağıdaki özelliklere sahiptir:

  • Bir örnek üyesi M, bir üye_erisim (§12.8.7) şeklinde referans alındığında, E.M, E adlı bir üyesi olan bir türün örneğini belirtir. E'nin bir türü ifade etmesi bağlama zamanı hatasıdır.
  • Sınıfın her örneği, sınıfın tüm örnek alanlarından oluşan ayrı bir küme içerir.
  • Bir örnek işlevi üyesi (yöntem, özellik, dizin oluşturucu, örnek oluşturucu veya sonlandırıcı) sınıfın belirli bir örneğinde çalışır ve bu örneğe (this) olarak erişilebilir.

Örnek: Aşağıdaki örnekte statik ve örnek üyelerine erişim kuralları gösterilmektedir:

class Test
{
    int x;
    static int y;
    void F()
    {
        x = 1;               // Ok, same as this.x = 1
        y = 1;               // Ok, same as Test.y = 1
    }

    static void G()
    {
        x = 1;               // Error, cannot access this.x
        y = 1;               // Ok, same as Test.y = 1
    }

    static void Main()
    {
        Test t = new Test();
        t.x = 1;       // Ok
        t.y = 1;       // Error, cannot access static member through instance
        Test.x = 1;    // Error, cannot access instance member through type
        Test.y = 1;    // Ok
    }
}

F yöntemi, örnek işlev üyesinde hem örnek üyelerine hem de statik üyelere erişmek için bir simple_name (§12.8.4) kullanılabileceğini gösterir. G yöntemi, statik işlev üyesinde bir simple_name aracılığıyla örnek üyesine erişmenin derleme zamanı hatası olduğunu gösterir. Yöntem, Main bir member_access (§12.8.7) örnek üyelerine örnekler aracılığıyla erişildiğini ve statik üyelere türler aracılığıyla erişildiğini gösterir.

son örnek

15.3.9 İç içe türler

15.3.9.1 Genel

Bir sınıf, yapı veya arabirim içinde bildirilen bir tür iç içe türü olarak adlandırılır. Derleme birimi veya ad alanı içinde bildirilen bir tür iç içe olmayan tür olarak adlandırılır.

Örnek: Aşağıdaki örnekte:

class A
{
    class B
    {
        static void F()
        {
            Console.WriteLine("A.B.F");
        }
    }
}

Sınıf B, sınıf A içinde bildirildiği için iç içe geçmiş bir türdür; sınıf A ise bir derleme birimi içinde bildirildiği için iç içe olmayan bir türdür.

son örnek

15.3.9.2 Tam nitelikli ad

İç içe tür bildiriminin tam adı (§7.8.3) S.N'dir. Burada S, tür bildirimi N'ün ilan edildiği tür bildiriminin tam adıdır ve N, iç içe tür bildiriminin nitelenmemiş adıdır (§7.8.2). Bu isim, her türlü generic_dimension_specifier (§12.8.18) dahil içerebilir.

15.3.9.3 Bildirilen erişilebilirlik

İç içe olmayan türler public veya internal olarak bildirilen erişilebilirliğe sahip olabilir ve varsayılan olarak internal olarak bildirilen erişilebilirliğe sahiptir. İç içe geçmiş türler, bu bildirilen erişilebilirlik biçimlerine ek olarak, içeren türün bir sınıf, yapı veya arabirim olmasına bağlı olarak bir veya daha fazla bildirilen erişilebilirlik biçimine de sahip olabilir:

  • Bir sınıfta bildirilen iç içe geçmiş bir tür, izin verilen türlerden herhangi birine sahip olabilir ve diğer sınıf üyeleri gibi, varsayılan olarak private bildirilen erişilebilirliği kullanır.
  • Bir yapıda bildirilen iç içe yerleştirilmiş tür, bildirilen erişilebilirlik (public, internalveya private) ve diğer yapı üyeleri gibi varsayılan olarak private bildirilen erişilebilirliği içeren üç biçimden herhangi birine sahip olabilir.

Örnek: Örnek

public class List
{
    // Private data structure
    private class Node
    {
        public object Data;
        public Node? Next;

        public Node(object data, Node? next)
        {
            this.Data = data;
            this.Next = next;
        }
    }

    private Node? first = null;
    private Node? last = null;

    // Public interface
    public void AddToFront(object o) {...}
    public void AddToBack(object o) {...}
    public object RemoveFromFront() {...}
    public object RemoveFromBack() {...}
    public int Count { get {...} }
}

özel bir iç içe sınıf Nodebildirir.

son örnek

15.3.9.4 Gizleniyor

İç içe yerleştirilmiş bir tür temel üyeyi gizleyebilir (§7.7.2.2). İç içe tür bildirimlerinde gizliliğin açıkça ifade edilebilmesi için değiştiriciye (new) izin verilir.

Örnek: Örnek

class Base
{
    public static void M()
    {
        Console.WriteLine("Base.M");
    }
}

class Derived: Base
{
    public new class M
    {
        public static void F()
        {
            Console.WriteLine("Derived.M.F");
        }
    }
}

class Test
{
    static void Main()
    {
        Derived.M.F();
    }
}

içinde Mtanımlanan yöntemi M gizleyen iç içe geçmiş bir sınıf Base gösterir.

son örnek

15.3.9.5 bu erişim

İç içe bir tür ve içeren türün this_access (§12.8.14) ile özel bir ilişkisi yoktur. Özellikle, this iç içe türün içinde, içeren türün örnek üyelerine başvurmak için kullanılamaz. İç içe bir türün, içeren türünün örnek üyelerine erişmesi gereken durumlarda, içeren türün bir örneği, iç içe türün oluşturucu bağımsız değişkeni olarak verilerek this ile erişim sağlanabilir.

Örnek: Aşağıdaki örnek

class C
{
    int i = 123;
    public void F()
    {
        Nested n = new Nested(this);
        n.G();
    }

    public class Nested
    {
        C this_c;

        public Nested(C c)
        {
            this_c = c;
        }

        public void G()
        {
            Console.WriteLine(this_c.i);
        }
    }
}

class Test
{
    static void Main()
    {
        C c = new C();
        c.F();
    }
}

bu tekniği gösterir. Bir C örneği, bir Nested örneği oluşturur ve bu örneği, Nested’nin oluşturucusuna geçirerek daha sonra C’nin örnek üyelerine erişim sağlar.

son örnek

15.3.9.6 İçeren türün özel ve korunan üyelerine erişim

İç içe yerleştirilmiş bir tür, içeren türünün erişilebilir olan tüm üyelerine erişim sağlar; buna, erişilebilirliği private ve protected şeklinde belirlenmiş olan içeren türün üyeleri de dahildir.

Örnek: Örnek

class C
{
    private static void F() => Console.WriteLine("C.F");

    public class Nested
    {
        public static void G() => F();
    }
}

class Test
{
    static void Main() => C.Nested.G();
}

Bir sınıf C içinde iç içe bir sınıf Nested gösterir. içindeNested, yöntemi G içinde Ftanımlanan statik yöntemi C çağırır ve F özel olarak bildirilen erişilebilirliği vardır.

son örnek

İç içe yerleştirilmiş bir tür, içeren türünün temel türünde tanımlanan korumalı üyelere de erişebilir.

Örnek: Aşağıdaki kodda

class Base
{
    protected void F() => Console.WriteLine("Base.F");
}

class Derived: Base
{
    public class Nested
    {
        public void G()
        {
            Derived d = new Derived();
            d.F(); // ok
        }
    }
}

class Test
{
    static void Main()
    {
        Derived.Nested n = new Derived.Nested();
        n.G();
    }
}

iç içe geçmiş sınıf Derived.Nested, F'nin temel sınıfı Derived'de tanımlanan Base korumalı yönteme, Derived bir örneği aracılığıyla çağırarak erişir.

son örnek

15.3.9.7 Genel sınıflarda iç içe türler

Genel bir sınıf bildirimi iç içe tür bildirimleri içerebilir. Kapsayan sınıfın tür parametreleri iç içe türler içinde kullanılabilir. İç içe tür bildirimi, yalnızca iç içe yerleştirilmiş türe uygulanan ek tür parametreleri içerebilir.

Genel sınıf bildiriminde yer alan her tür bildirimi örtük olarak genel tür bildirimidir. Genişletilebilir bir türün içinde iç içe yerleştirilmiş bir türe referans yazarken, tür bağımsız değişkenleri de dahil olmak üzere içeren türe ait isim belirtilmelidir. Ancak, dış sınıfın içinden iç içe geçmiş tür niteleme olmadan kullanılabilir; dış sınıfın örnek türü, iç içe geçmiş tür oluşturulurken dolaylı olarak kullanılabilir.

Örnek: Aşağıda oluşturulmuş bir türe başvurmak için üç farklı doğru yolu gösterilmektedir; ilk ikisi eşdeğerdir:

class Outer<T>
{
    class Inner<U>
    {
        public static void F(T t, U u) {...}
    }

    static void F(T t)
    {
        Outer<T>.Inner<string>.F(t, "abc");    // These two statements have
        Inner<string>.F(t, "abc");             // the same effect
        Outer<int>.Inner<string>.F(3, "abc");  // This type is different
        Outer.Inner<string>.F(t, "abc");       // Error, Outer needs type arg
    }
}

son örnek

Kötü programlama stili olsa da, iç içe bir türdeki tür parametresi, dış türde bildirilen bir üyeyi veya tür parametresini gizleyebilir.

Örnek:

class Outer<T>
{
    class Inner<T>                                  // Valid, hides Outer's T
    {
        public T t;                                 // Refers to Inner's T
    }
}

son örnek

15.3.10 Rezerve üye adları

15.3.10.1 Genel

Temel alınan C# çalışma zamanı uygulamasını kolaylaştırmak için, bir özellik, olay veya dizin oluşturucu olan her kaynak üye bildirimi için uygulama, üye bildiriminin türüne, adına ve türüne (§15.3.10.2, §15.3.10.3, §15.3.10.4) göre iki yöntem imzası ayırmalıdır. Temel çalışma zamanı uygulaması bu rezervasyonları kullanmasa bile, bir programın imzası aynı kapsamda bildirilen bir üye tarafından ayrılmış imzayla eşleşen bir üyeyi bildirmesi derleme zamanı hatasıdır.

Rezerve edilmiş adlar bildirim oluşturmaz, bu nedenle üye aramalarına katılmaz. Ancak, bildirimin ilişkili ayrılmış yöntem imzaları devralma işlemine katılır (§15.3.4) ve değiştirici (§15.3.5) ile gizlenebilirnew.

Not: Bu adların rezervasyonu üç amaca hizmet eder:

  1. C# dil özelliğine get veya set metodu erişimi sağlamak için altyapının sıradan bir tanımlayıcıyı yöntem adı olarak kullanmasına izin vermek.
  2. Diğer dillerin C# dili özelliğine erişim elde etmek veya erişim ayarlamak için yöntem adı olarak normal bir tanımlayıcı kullanarak birlikte çalışmalarına izin vermek için.
  3. Ayrılmış üye adlarının ayrıntılarını tüm C# uygulamalarında tutarlı hale getirerek, uyumlu bir derleyici tarafından kabul edilen kaynağın başka bir derleyici tarafından kabul edilmesini sağlamaya yardımcı olmak için.

son not

Sonlandırıcının (§15.13) bildirimi, imza ayrılmasını da sağlar (§15.3.10.5).

Belirli adlar işleç yöntemi adları olarak kullanılmak üzere ayrılmıştır (§15.3.10.6).

15.3.10.2 Özellikler için ayrılmış üye adları

Türü P olan bir (T) özelliği için aşağıdaki imzalar ayrılmıştır:

T get_P();
void set_P(T value);

Özellik salt okunur veya salt okunur olsa bile her iki imza da ayrılmıştır.

Örnek: Aşağıdaki kodda

class A
{
    public int P
    {
        get => 123;
    }
}

class B : A
{
    public new int get_P() => 456;

    public new void set_P(int value)
    {
    }
}

class Test
{
    static void Main()
    {
        B b = new B();
        A a = b;
        Console.WriteLine(a.P);
        Console.WriteLine(b.P);
        Console.WriteLine(b.get_P());
    }
}

Sınıf A, salt okunur bir özellik P tanımlar ve get_P ile set_P yöntemleri için imzaları rezerve eder. A sınıfı B , bu ayrılmış imzaların her ikisinden A de türetilir ve gizler. Örnek çıkışı oluşturur:

123
123
456

son örnek

15.3.10.3 Olaylar için ayrılmış üye adları

Temsilci türünde bir E olayı §15.8 için aşağıdaki imzalar ayrılmıştır:

void add_E(T handler);
void remove_E(T handler);

15.3.10.4 Dizin oluşturucular için ayrılmış üye adları

parametre listesi olan T türünde bir dizin oluşturucu (L) için aşağıdaki imzalar ayrılmıştır:

T get_Item(L);
void set_Item(L, T value);

Dizleyici salt okunur veya salt yazılır olsa bile her iki imza da ayrılmıştır.

Ayrıca üye adı Item rezerv edilmiştir.

15.3.10.5 Sonlandırıcılar için ayrılmış üye adları

Sonlandırıcı (§15.13) içeren bir sınıf için aşağıdaki imza ayrılmıştır:

void Finalize();

15.3.10.6 İşleçler için ayrılmış yöntem adları

Aşağıdaki yöntem adları ayrılmıştır. Birçoğunun bu belirtimde karşılık gelen işleçleri olsa da, bazıları gelecek sürümler tarafından kullanılmak üzere, bazıları ise diğer dillerle birlikte çalışma için ayrılmıştır.

Yöntem Adı C# İşleci
op_Addition + (ikili)
op_AdditionAssignment (ayrılmış)
op_AddressOf (ayrılmış)
op_Assign (ayrılmış)
op_BitwiseAnd & (ikili)
op_BitwiseAndAssignment (ayrılmış)
op_BitwiseOr \|
op_BitwiseOrAssignment (ayrılmış)
op_CheckedAddition (gelecekteki kullanım için ayrılmıştır)
op_CheckedDecrement (gelecekteki kullanım için ayrılmıştır)
op_CheckedDivision (gelecekteki kullanım için ayrılmıştır)
op_CheckedExplicit (gelecekteki kullanım için ayrılmıştır)
op_CheckedIncrement (gelecekteki kullanım için ayrılmıştır)
op_CheckedMultiply (gelecekteki kullanım için ayrılmıştır)
op_CheckedSubtraction (gelecekteki kullanım için ayrılmıştır)
op_CheckedUnaryNegation (gelecekteki kullanım için ayrılmıştır)
op_Comma (ayrılmış)
op_Decrement -- (ön ek ve sonek)
op_Division /
op_DivisionAssignment (ayrılmış)
op_Equality ==
op_ExclusiveOr ^
op_ExclusiveOrAssignment (ayrılmış)
op_Explicit açık (daraltma) zorlama
op_False false
op_GreaterThan >
op_GreaterThanOrEqual >=
op_Implicit örtük (genişletme) zorlaması
op_Increment ++ (ön ek ve sonek)
op_Inequality !=
op_LeftShift <<
op_LeftShiftAssignment (ayrılmış)
op_LessThan <
op_LessThanOrEqual <=
op_LogicalAnd (ayrılmış)
op_LogicalNot !
op_LogicalOr (ayrılmış)
op_MemberSelection (ayrılmış)
op_Modulus %
op_ModulusAssignment (ayrılmış)
op_MultiplicationAssignment (ayrılmış)
op_Multiply * (ikili)
op_OnesComplement ~
op_PointerDereference (ayrılmış)
op_PointerToMemberSelection (ayrılmış)
op_RightShift >>
op_RightShiftAssignment (ayrılmış)
op_SignedRightShift (ayrılmış)
op_Subtraction - (ikili)
op_SubtractionAssignment (ayrılmış)
op_True true
op_UnaryNegation - (tek terimli)
op_UnaryPlus + (tek terimli)
op_UnsignedRightShift (gelecekteki kullanım için ayrılmıştır)
op_UnsignedRightShiftAssignment (ayrılmış)

15.4 Sabitleri

Sabit, bir sabit değeri temsil eden bir sınıf üyesidir: derleme zamanında hesaplanabilir bir değer. bir constant_declaration , belirli bir türe ait bir veya daha fazla sabiti tanıtır.

constant_declaration
    : attributes? constant_modifier* 'const' type constant_declarators ';'
    ;

constant_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    ;

constant_declaration bir öznitelik kümesi (§23), new değiştirici (§15.3.5) ve izin verilen bildirilen erişilebilirlik türlerinden birini (§15.3.6) içerebilir. Öznitelikler ve değiştiriciler, constant_declaration tarafından bildirilen tüm üyeler için geçerlidir. Sabitler statik üyeler olarak kabul edilse de, constant_declaration bir değiştirici gerektirmez veya izin vermezstatic. Aynı değiştiricinin sabit bir bildirimde birden çok kez görünmesi bir hatadır.

constant_declaration türü, bildirim tarafından tanıtılan üyelerin türünü belirtir. Türden sonra, her biri yeni bir üyeyi tanıtan constant_declarator listesi (§13.6.3) yer alır. constant_declarator, üyeyi adlandıran bir tanımlayıcı, ardından "=" belirteci ve ardından üyenin değerini veren bir constant_expression (§12.25) oluşur.

Sabit bildirimde belirtilen türsbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, bir enum_türü veya bir referans_türü olmalıdır. Her constant_expression, hedef türün veya örtük dönüştürmeyle (§10.2) hedef türüne dönüştürülebilecek bir türün değerini vermelidir.

Sabitin türü en az sabitin kendisi kadar erişilebilir olmalıdır (§7.5.5).

Bir sabitin değeri bir simple_name (§12.8.4) veya bir member_access (§12.8.7) kullanılarak bir ifadede elde edilir.

Sabit bir değer, bir sabit_ifadesi içinde yer alabilir. Bu nedenle, sabit bir constant_expression gerektiren herhangi bir yapıda kullanılabilir.

Not: Bu tür yapılara örnek olarak case etiketler, deyimler, goto caseenum üye bildirimleri, öznitelikler ve diğer sabit bildirimler verilebilir. son not

Not: §12.25'te açıklandığı gibi , constant_expression derleme zamanında tam olarak değerlendirilebilen bir ifadedir. Bir reference_type'ın dışında null olmayan bir değer oluşturmanın tek yolu string operatörünü uygulamak olduğundan ve new'da new operatörüne izin verilmediğinden, dışındaki reference_typeler için tek olası değer string olur. son not

Sabit bir değer için sembolik ad istendiğinde, ancak bu değerin türüne sabit bir bildirimde izin verilmediğinde veya değer bir constant_expression tarafından derleme zamanında hesaplanmadığında, bunun yerine salt okunur bir alan (§15.5.3) kullanılabilir.

Not: sürüm oluşturma semantiği const ve readonly farklıdır (§15.5.3.3). son not

Birden çok sabit bildiren sabit bildirimi, aynı özniteliklere, değiştiricilere ve türe sahip tek sabitlerin birden çok bildirimine eşdeğerdir.

Örnek:

class A
{
    public const double X = 1.0, Y = 2.0, Z = 3.0;
}

eşdeğerdir

class A
{
    public const double X = 1.0;
    public const double Y = 2.0;
    public const double Z = 3.0;
}

son örnek

Bağımlılıklar döngüsel nitelikte olmadığı sürece sabitlerin aynı program içindeki diğer sabitlere bağımlı olmasına izin verilir.

Örnek: Aşağıdaki kodda

class A
{
    public const int X = B.Z + 1;
    public const int Y = 10;
}

class B
{
    public const int Z = A.Y + 1;
}

Derleyicinin önce A.Ydeğerlendirmesi, ardından B.Zve son olarak A.Xdeğerlendirmesi gerekir ve 10, 11ve 12değerlerini üretmelidir.

son örnek

Sabit bildirimler diğer programlardan sabitlere bağlı olabilir, ancak bu tür bağımlılıklar yalnızca bir yönde mümkündür.

Örnek: Yukarıdaki örnekte, eğer ve A ayrı programlarda bildirildiyse, mümkün olabilir ki BA.X öğesine bağımlı olsun, ancak B.Z aynı anda B.Z öğesine bağımlı olamaz. son örnek

15.5 Alanlar

15.5.1 Genel

Alan, bir nesne veya sınıfla ilişkili bir değişkeni temsil eden bir üyedir. field_declaration, belirli bir türe ait bir veya daha fazla alanı tanıtır.

field_declaration
    : attributes? field_modifier* type variable_declarators ';'
    ;

field_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'readonly'
    | 'volatile'
    | unsafe_modifier   // unsafe code support
    ;

variable_declarators
    : variable_declarator (',' variable_declarator)*
    ;

variable_declarator
    : identifier ('=' variable_initializer)?
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

field_declaration bir öznitelik kümesi (§23), new değiştirici (§15.3.5), dört erişim değiştiricisinin (§15.3.6) geçerli bir bileşimi ve değiştirici static (§15.5.2) içerebilir. Ayrıca, bir field_declaration bir readonly değiştirici (§15.5.3) veya bir volatile değiştirici (§15.5.4) içerebilir, ancak her ikisini birden içeremez. Öznitelikler ve değiştiriciler, field_declaration tarafından bildirilen tüm üyeler için geçerlidir. Aynı değiştiricinin bir field_declaration birden çok kez görünmesi bir hatadır.

field_declaration türü, bildirim tarafından tanıtılan üyelerin türünü belirtir. Türden sonra, yeni bir üyeyi tanıtan her bir variable_declarator içeren bir liste gelir. Bir değişken_tanılayıcı, o üyeyi adlandıran bir tanımlayıcı, isteğe bağlı olarak bir "=" belirteci ve o üyenin ilk değerini veren bir değişken_initializeleri (§15.5.6) içerir.

Bir alanın türü en az alanın kendisi kadar erişilebilir olmalıdır (§7.5.5).

Bir alanın değeri bir simple_name (§12.8.4), member_access (§12.8.7) veya base_access (§12.8.15) kullanılarak bir ifadede elde edilir. Salt okunur olmayan bir alanın değeri atama kullanılarak değiştirilir (§12.23). Salt okunur olmayan bir alanın değeri, sonek artırma ve azaltma işleçleri (§12.8.16) ve ön ek artırma ve azaltma işleçleri (§12.9.7) kullanılarak hem alınabilir hem de değiştirilebilir.

Birden çok alan bildiren alan bildirimi, aynı özniteliklere, değiştiricilere ve türe sahip tek alanların birden çok bildirimine eşdeğerdir.

Örnek:

class A
{
    public static int X = 1, Y, Z = 100;
}

eşdeğerdir

class A
{
    public static int X = 1;
    public static int Y;
    public static int Z = 100;
}

son örnek

15.5.2 Statik ve örnek alanları

Alan bildirimi bir static değiştirici içerdiğinde, bildirimi tarafından tanıtılan alanlar statik alanlardır. Değiştirici olmadığında static , bildirim tarafından sunulan alanlar örnek alanlarıdır. Statik alanlar ve örnek alanları, C# tarafından desteklenen çeşitli değişken türlerinden (§9) ikisidir ve bazen sırasıyla statik değişkens ve örnek değişkenis olarak adlandırılır.

§15.3.8'de açıklandığı gibi, sınıfın her örneği sınıfın örnek alanlarının eksiksiz bir kümesini içerirken, sınıfın veya kapalı oluşturulmuş türün örnek sayısından bağımsız olarak her genel olmayan veya kapalı oluşturulmuş tür için yalnızca bir statik alan kümesi vardır.

15.5.3 Salt okunur alanlar

15.5.3.1 Genel

bir field_declaration değiştirici readonly içerdiğinde, bildirim tarafından tanıtılan alanlar salt okunur alanlardır. Salt okunur alanlara doğrudan atamalar yalnızca bu bildirimin bir parçası olarak veya aynı sınıftaki bir örnek oluşturucusunda veya statik oluşturucuda gerçekleşebilir. (Salt okunur bir alan bu bağlamlarda birden çok kez atanabilir.) Özellikle, salt okunur bir alana doğrudan atamalara yalnızca aşağıdaki bağlamlarda izin verilir:

  • Alanı tanıtan variable_declarator (bildirime bir variable_initializer ekleyerek).
  • Bir örnek alanı için, yerel işlevler ve anonim işlevler hariç olmak üzere alan bildirimini içeren sınıfın örnek oluşturucularında ve yalnızca oluşturulan örnekte. Statik bir alan için, yerel işlevler ve anonim işlevler hariç olmak üzere, alan bildirimini içeren sınıftaki statik oluşturucuda veya statik bir alan veya özellik başlatıcıda. Bunlar aynı zamanda salt okunur bir alanı çıkış veya başvuru parametresi olarak geçirmenin geçerli olduğu tek bağlamlardır.

Salt okunur alanlara atama yapmak veya başka bir bağlamda çıktı veya referans parametresi olarak geçirmeye çalışmak, derleme zamanı hatasına neden olur.

15.5.3.2 Sabitler için statik salt okunur alanlar kullanma

Sabit bir değer için simgesel ad istendiğinde, ancak sabit bir bildirimde değerin türüne izin verilmediğinde veya değer derleme zamanında hesaplanamadığında, statik salt okunur alan yararlıdır.

Örnek: Aşağıdaki kodda

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);

    private byte red, green, blue;

    public Color(byte r, byte g, byte b)
    {
        red = r;
        green = g;
        blue = b;
    }
}

Blackdeğerleri derleme zamanında hesaplanamadığından , WhiteRed, , Greenve Blue üyeleri const üyeleri olarak bildirilemez. Ancak, bunları static readonly bildirmenin etkisi çok daha fazladır.

son örnek

15.5.3.3 Sabitlerin ve statik salt okunur alanların sürümlemesi

Sabitler ve salt okunur alanlar farklı ikili sürüm oluşturma semantiğine sahiptir. bir ifade bir sabite başvurduğunda, sabitin değeri derleme zamanında elde edilir, ancak bir ifade salt okunur bir alana başvurduğunda, alanın değeri çalışma zamanına kadar alınmaz.

Örnek: İki ayrı programdan oluşan bir uygulamayı düşünün:

namespace Program1
{
    public class Utils
    {
        public static readonly int x = 1;
    }
}

ve

namespace Program2
{
    class Test
    {
        static void Main()
        {
            Console.WriteLine(Program1.Utils.X);
        }
    }
}

Program1 ve Program2 ad alanları, ayrı olarak derlenen iki programı belirtir. Program1.Utils.X, bir alan olarak static readonly bildirildiğinden, Console.WriteLine deyimindeki değer çıkışı derleme zamanında bilinmez, bunun yerine çalışma zamanında elde edilir. Bu nedenle, değer X değiştirilirse ve Program1 yeniden derlenirse, Console.WriteLine deyimi Program2 yeniden derlenmese bile yeni değerin çıkışını verir. Ancak X bir sabit olsaydı, X'in değeri Program2 derlendiği sırada elde edilirdi ve Program1 yeniden derlenene kadar, Program2'deki değişikliklerden etkilenmezdi.

son örnek

15.5.4 Geçici alanlar

bir field_declaration değiştirici volatile içerdiğinde, bu bildirim tarafından sunulan alanlar geçici alanlardır. Geçici olmayan alanlar için, yönergeleri yeniden sıralayan iyileştirme teknikleri, lock_statement (§13.13) tarafından sağlanan gibi eşitleme olmadan alanlara erişen çok iş parçacıklı programlarda beklenmeyen ve öngörülemeyen sonuçlara yol açabilir. Bu iyileştirmeler derleyici, çalışma zamanı sistemi veya donanım tarafından gerçekleştirilebilir. Geçici alanlar için bu tür yeniden sıralama iyileştirmeleri kısıtlanır:

  • Geçici bir alanın okunmasına geçici okuma denir. Volatile bir okumada "okuma semantik edinimi" vardır; yani, komut sırasındaki okuma işleminden sonra gerçekleşen bellek başvurularından önce gerçekleşmesi garanti edilir.
  • Geçici bir alanın yazma işlemi geçici yazma olarak adlandırılır. Geçici yazmada "yayın semantiği" vardır; diğer bir ifadeyle, yönerge dizisindeki yazma yönergesi öncesinde herhangi bir bellek başvurusundan sonra gerçekleşmesi garanti edilir.

Bu kısıtlamalar, tüm iş parçacıklarının diğer iş parçacıkları tarafından gerçekleştirilen uçucu yazmaları gerçekleştirdikleri sırayla kaydetmesini sağlar. Tüm yürütme iş parçacıklarından görüldüğü gibi geçici yazmaların tek bir toplam sırasını sağlamak için uyumlu bir uygulama gerekli değildir. Geçici bir alanın türü aşağıdakilerden biri olmalıdır:

  • bir reference_type.
  • Referans türü olarak bilinen bir type_parameter (§15.2.5).
  • byte türü, sbyte türü, short türü, ushort türü, int türü, uint türü, char türü, float türü, bool türü veya System.IntPtr türüSystem.UIntPtr.
  • enum_type türü, enum_base olan byte, sbyte, short, ushort, int veya uint.

Örnek: Örnek

class Test
{
    public static int result;
    public static volatile bool finished;

    static void Thread2()
    {
        result = 143;
        finished = true;
    }

    static void Main()
    {
        finished = false;

        // Run Thread2() in a new thread
        new Thread(new ThreadStart(Thread2)).Start();    

        // Wait for Thread2() to signal that it has a result
        // by setting finished to true.
        for (;;)
        {
            if (finished)
            {
                Console.WriteLine($"result = {result}");
                return;
            }
        }
    }
}

çıktıyı üretir:

result = 143

Bu örnekte, yöntemi yöntemini MainThread2çalıştıran yeni bir iş parçacığı başlatır. Bu yöntem, bir değeri result adlı geçici olmayan bir alanda depolar, sonra true adlı geçici alanda finished depolar. Ana iş parçacığı alanın finished olarak ayarlanmasını truebekler ve alanını resultokur. Ana iş parçacığı, finished bildirildikten volatile sonra 143 alanından result değerini okuyacaktır. Alanı finished bildirilmemişsevolatile, deponun result deposundan sonra finishedalanından result0 değerini okumasına izin verilebilirdi. finished'yu volatile alanı olarak ilan etmek, bu tür tutarsızlıkları önler.

son örnek

15.5.5 Alan başlatma

İster statik alan ister örnek alanı olsun, alanın ilk değeri, alan türünün varsayılan değeridir (§9,3). Bu varsayılan başlatma gerçekleşmeden önce bir alanın değerini gözlemlemek mümkün değildir ve bu nedenle bir alan hiçbir zaman "başlatılmamış" olmaz.

Örnek: Örnek

class Test
{
    static bool b;
    int i;

    static void Main()
    {
        Test t = new Test();
        Console.WriteLine($"b = {b}, i = {t.i}");
    }
}

çıktıyı üretir

b = False, i = 0

çünkü b ve i her ikisi de varsayılan değerlere otomatik olarak başlatılır.

son örnek

15.5.6 Değişken başlatıcılar

15.5.6.1 Genel

Alan bildirimleri variable_initializeriçerebilir. Statik alanlar için değişken başlatıcılar, sınıf başlatma sırasında yürütülen atama deyimlerine karşılık gelir. Örnek alanları için değişken başlatıcılar, sınıfın bir örneği oluşturulduğunda yürütülen atama deyimlerine karşılık gelir.

Örnek: Örnek

class Test
{
    static double x = Math.Sqrt(2.0);
    int i = 100;
    string s = "Hello";

    static void Main()
    {
        Test a = new Test();
        Console.WriteLine($"x = {x}, i = {a.i}, s = {a.s}");
    }
}

çıktıyı üretir

x = 1.4142135623730951, i = 100, s = Hello

çünkü statik alan başlatıcıları yürütüldüğünde x'ye bir atama gerçekleşir ve örnek alan başlatıcıları yürütüldüğünde i ve s'ye atamalar gerçekleşir.

son örnek

§15.5.5'te açıklanan varsayılan değer başlatma işlemi, değişken başlatıcısı olan alanlar da dahil olmak üzere tüm alanlar için gerçekleşir. Bu nedenle, bir sınıf başlatıldığında, bu sınıftaki tüm statik alanlar ilk olarak varsayılan değerlerine başlatılır ve ardından statik alan başlatıcıları metin sırasına göre yürütülür. Benzer şekilde, bir sınıfın örneği oluşturulduğunda, bu örnekteki tüm örnek alanları ilk olarak varsayılan değerlerine başlatılır ve ardından örnek alanı başlatıcıları metin sırasına göre yürütülür. Aynı tür için birden çok kısmi tür bildiriminde alan bildirimleri olduğunda, parçaların sırası belirtilmez. Ancak, her bölümde alan başlatıcıları sırayla yürütülür.

Değişken başlatıcıları olan statik alanların varsayılan değer durumlarında gözlemlenmeleri mümkündür.

Örnek: Ancak, bu kesinlikle stil meselesi olarak önerilmez. Örnek

class Test
{
    static int a = b + 1;
    static int b = a + 1;

    static void Main()
    {
        Console.WriteLine($"a = {a}, b = {b}");
    }
}

bu davranışı sergiler. a ve b döngüsel tanımlarına rağmen, program geçerlidir. Çıkış üretir

a = 1, b = 2

çünkü başlatıcıları çalıştırılmadan önce statik alanlar a ve b, 0 için varsayılan değer olan int ile başlatılır. Başlatıcı a çalıştırıldığında, b değeri sıfır olur ve bu nedenle a, 1 olarak başlatılır. b başlatıcı çalıştırıldığında, a'nın değeri zaten 1 olur ve bu nedenle b, 2 olarak başlatılır.

son örnek

15.5.6.2 Statik alan başlatma

Bir sınıfın statik alan değişkeni başlatıcıları, sınıf bildiriminde göründükleri metin sırasına göre yürütülen atama dizisine karşılık gelir (§15.5.6.1). Kısmi bir sınıf içinde "metin düzeni" anlamı §15.5.6.1 tarafından belirtilir. Sınıfında statik bir oluşturucu (§15.12) varsa, statik alan başlatıcılarının yürütülmesi bu statik oluşturucu yürütülmadan hemen önce gerçekleşir. Aksi takdirde, statik alan başlatıcıları bu sınıfın statik alanının ilk kullanımından önce uygulamaya bağımlı bir zamanda yürütülür.

Örnek: Örnek

class Test
{
    static void Main()
    {
        Console.WriteLine($"{B.Y} {A.X}");
    }

    public static int F(string s)
    {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    public static int X = Test.F("Init A");
}

class B
{
    public static int Y = Test.F("Init B");
}

aşağıdaki sonuçlardan birini üretebilir:

Init A
Init B
1 1

veya sonuç:

Init B
Init A
1 1

X'nin başlatıcısının ve Y'nin başlatıcısının yürütülmesi her iki sırada da gerçekleşebilir; yalnızca bu alanlara yapılan başvurulardan önce gerçekleşmesi gerekmektedir. Ancak örnekte:

class Test
{
    static void Main()
    {
        Console.WriteLine($"{B.Y} {A.X}");
    }

    public static int F(string s)
    {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    static A() {}
    public static int X = Test.F("Init A");
}

class B
{
    static B() {}
    public static int Y = Test.F("Init B");
}

çıkış şu şekilde olacaktır:

Init B
Init A
1 1

çünkü statik oluşturucuların ne zaman yürütüldüğünün kuralları (§15.12'de tanımlandığı gibi) B'nin statik oluşturucusunun (ve dolayısıyla Bstatik alan başlatıcılarının) statik oluşturucusu ve alan başlatıcıları öncesinde Açalıştırılması gerekir.

son örnek

15.5.6.3 Örnek alanı başlatma

Bir sınıfın örnek alan değişkeni başlatıcıları, bu sınıfın örnek oluşturucularından birine (§15.11.3) girdikten hemen sonra yürütülen bir atama dizisine karşılık gelir. Kısmi bir sınıf içinde "metin düzeni" anlamı §15.5.6.1 tarafından belirtilir. Değişken başlatıcıları, sınıf bildiriminde (§15.5.6.1) göründükleri metin sırasına göre yürütülür. Sınıf örneği oluşturma ve başlatma işlemi §15.11'de daha ayrıntılı olarak açıklanmıştır.

Örnek alanı için değişken başlatıcı, oluşturulan örneğe başvuramaz. Bu nedenle, bir değişken başlatıcısında this'ye başvurmak bir derleme zamanı hatasıdır, çünkü bir değişken başlatıcının simple_name aracılığıyla herhangi bir örnek üyesine başvurması da bir derleme zamanı hatasıdır.

Örnek: Aşağıdaki kodda

class A
{
    int x = 1;
    int y = x + 1;     // Error, reference to instance member of this
}

değişken başlatıcısı y , oluşturulan örneğin bir üyesine başvuracağından derleme zamanı hatasıyla sonuçlanır.

son örnek

15.6 Yöntemleri

15.6.1 Genel

§15.6 ve alt modülleri sınıflardaki yöntem bildirimlerini kapsar. Bu metin, yapılardaki (§16.4) ve arabirimlerdeki (§19.4.3) yöntemleri bildirme hakkında bilgilerle genişletilir.

Yöntem, bir nesne veya sınıf tarafından gerçekleştirilebilecek bir hesaplama veya eylem uygulayan bir üyedir. Yöntemler method_declarationkullanılarak bildirilir:

method_declaration
    : attributes? method_modifiers return_type method_header method_body
    | attributes? ref_method_modifiers ref_kind ref_return_type method_header
      ref_method_body
    ;

method_modifiers
    : method_modifier* 'partial'?
    ;

ref_kind
    : 'ref'
    | 'ref' 'readonly'
    ;

ref_method_modifiers
    : ref_method_modifier*
    ;

method_header
    : member_name '(' parameter_list? ')'
    | member_name type_parameter_list '(' parameter_list? ')'
      type_parameter_constraints_clause*
    ;

method_modifier
    : ref_method_modifier
    | 'async'
    ;

ref_method_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'readonly'        // direct struct members only
    | unsafe_modifier   // unsafe code support
    ;

return_type
    : ref_return_type
    | 'void'
    ;

ref_return_type
    : type
    ;

member_name
    : identifier
    | interface_type '.' identifier
    ;

method_body
    : block
    | '=>' null_conditional_invocation_expression ';'
    | '=>' expression ';'
    | ';'
    ;

ref_method_body
    : block
    | '=>' 'ref' variable_reference ';'
    | ';'
    ;

Dil bilgisi notları:

  • unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.
  • bir method_body tanındığında, null_conditional_invocation_expression ve ifade alternatifleri geçerliyse, ilki seçilir.

Not: Buradaki alternatiflerin çakışması ve önceliği yalnızca açıklayıcı kolaylık sağlamak içindir; dilbilgisi kuralları çakışmayı kaldırmak için ayrıntılı olarak açıklanabilir. ANTLR ve diğer dil bilgisi sistemleri aynı kolaylıkları benimseyerek method_body belirtilen semantiği otomatik olarak kullanır. son not

method_declaration bir dizi öznitelik (§23) ve izin verilen türlerde bildirilen erişilebilirlik (§15.3.6),new (§15.3.5),static (§15.6.3), (§15.6.3) virtual (§15.6.4), override (§15.6.5), sealed (§15.6.6), abstract (§15.6.7), extern (§15.6.8) ve async (§15.14). Ayrıca, doğrudan bir struct_declaration tarafından içeren bir method_declaration, readonly değiştiricisini içerebilir (§16.4.12).

Aşağıdakilerin tümü doğruysa bildirimin geçerli bir değiştirici bileşimi vardır:

  • Bildirim, erişim değiştiricilerinin (§15.3.6) geçerli bir bileşimini içerir.
  • Bildirim aynı değiştiriciyi birden çok kez içermez.
  • Bildirim, şu değiştiricilerden en fazla birini içerir: static, virtualve override.
  • Bildirimi aşağıdaki değiştiricilerden en çoğunu içerir: new ve override.
  • Bildirim değiştiriciyi abstract içeriyorsa, bildirim şu değiştiricilerden hiçbirini içermez: static, virtual, sealedveya extern.
  • Bildirim, soyut üyenin bir sanal üyeyi abstract geçersiz kılabilmesi için ve override değiştiricilerini içerebilir.
  • Bildirim değiştiriciyi private içeriyorsa, bildirim şu değiştiricilerden hiçbirini içermez: virtual, overrideveya abstract.
  • Bildirim değiştiriciyi sealed içeriyorsa, bildirim değiştiriciyi override de içerir.
  • Bildirim değiştiriciyi partial içeriyorsa, şu değiştiricilerden hiçbirini içermez: new, public, protected, internal, , private, virtual, , sealedoverride, abstractveya extern.

Yöntemler, eğer herhangi bir şey döndürüyorlarsa ne döndüreceklerine göre sınıflandırılır.

  • Eğer ref varsa, yöntem referansla döndürme şeklindedir ve isteğe bağlı olarak salt okunur olabilen bir değişken referansı döndürür.
  • Aksi takdirde, return_type voidyöntemi return-no-value olur ve bir değer döndürmez;
  • Aksi takdirde yöntem, değer döndüren bir yöntemdir ve bir değer döndürür.

Return_type, değere göre döndüren veya değer döndürmeyen bir yöntem bildiriminin, yöntem tarafından döndürülen sonucun türünü (varsa) belirtir. Yalnızca değer döndürmeyen bir yöntem, partial değiştiricisini içerebilir (§15.6.9). Bildirim değiştiriciyi async içeriyorsa return_type veya void yöntem değere göre döndürür ve dönüş türü bir görev türüdür (§15.14.1).

Ref ile dönen yöntem bildiriminin ref_return_type, yöntemin döndüğü variable_reference tarafından başvurulan değişkenin türünü belirtir.

Genel yöntem, bildirimi type_parameter_list içeren bir yöntemdir. Bu yöntem için tür parametrelerini belirtir. İsteğe bağlı type_parameter_constraints_clause, tür parametreleri için kısıtlamaları belirtir.

Değiştirici ile veya açık arabirim üyesi uygulaması için genel bir override, geçersiz kılınan yöntemden veya arabirim üyesinden tür parametresi kısıtlamalarını devralır. Bu tür bildirimlerin yalnızca primary_constraintve değerlerini içeren classolabilir. structBu bağlamda anlamı sırasıyla yöntemleri ve açık arabirim uygulamalarını geçersiz kılmaya yönelik olarak §15.6.5 ve §19.6.2'de tanımlanmıştır.

member_name yönteminin adını belirtir. Yöntem açık bir arabirim üyesi uygulaması (§19.6.2) değilse , member_name yalnızca bir tanımlayıcıdır.

Açık arabirim üyesi uygulaması için, member_name bir interface_type ve ardından bir "." ve bir tanımlayıcıdan oluşur. Bu durumda, bildirimde (muhtemelen) extern veya asyncdışında hiçbir değiştirici bulunacaktır.

İsteğe bağlı parameter_list yöntemin parametrelerini belirtir (§15.6.2).

return_type veya ref_return_type ve parameter_list'te başvurulan türlerin her biri, en az yöntemin kendisi kadar erişilebilir olmalıdır (§7.5.5).

Değer döndüren veya değer döndürmeyen bir yöntemin yöntem_gövdesi ya bir noktalı virgül, blok gövdesi veya ifade gövdesidir. Blok gövdesi, yöntem çağrıldığında yürütülecek deyimleri belirten bir bloktan oluşur. İfade gövdesi, => ifadesiyle başlar, ardından bir null_conditional_invocation_expression veya ifade gelir ve noktalı virgülle sona erer; yöntem çağrıldığında gerçekleştirilecek tek bir ifadeyi belirtir.

Soyut ve extern yöntemleri için method_body yalnızca noktalı virgülden oluşur. Kısmi yöntemler için method_body noktalı virgül, blok gövdesi veya ifade gövdesinden oluşabilir. Diğer tüm yöntemler için method_body bir blok gövdesi veya ifade gövdesidir.

method_body noktalı virgülden oluşuyorsa, bildirim değiştiriciyi async içermez.

Referansla dönen bir yöntemin ref_method_body'si noktalı virgül, blok gövdesi veya ifade gövdesinden biri olabilir. Blok gövdesi, yöntem çağrıldığında yürütülecek deyimleri belirten bir bloktan oluşur. İfade gövdesi, => ile başlar, ardından ref, bir variable_reference ve bir noktalı virgül bulunur ve bu, yöntem çağrıldığında değerlendirilecek tek bir variable_reference belirtir.

Soyut ve extern yöntemleri için, ref_method_body yalnızca noktalı virgülden oluşur; diğer tüm yöntemler için ref_method_body bir blok gövdesi veya ifade gövdesidir.

Ad, tür parametrelerinin sayısı ve bir yöntemin parametre listesi yöntemin imzasını (§7.6) tanımlar. Özellikle, bir yöntemin imzası adı, tür parametrelerinin sayısı ve sayı, parameter_mode_modifier s (§15.6.2.1) ve parametrelerinintürlerinden oluşur. Dönüş türü, bir yöntemin imzasının parçası değildir; aynı şekilde parametrelerin adları, tür parametrelerinin adları ve kısıtlamalar da imzanın parçası değildir. Parametre türü yöntemin tür parametresine başvurduğunda, tür parametresinin sıralı konumu (tür parametresinin adı değil) tür denkliği için kullanılır.

Bir yöntemin adı, aynı sınıfta bildirilen diğer tüm yöntem dışı yöntemlerin adlarından farklı olmalıdır. Ayrıca, bir yöntemin imzası aynı sınıfta bildirilen diğer tüm yöntemlerin imzalarından farklı olacaktır ve aynı sınıfta bildirilen iki yöntem yalnızca , inve outile reffarklı imzalara sahip olmayacaktır.

Yöntemin type_parametermethod_declaration genelinde kapsam dahilindedir ve return_type veya ref_return_type,method_body veya ref_method_body ve type_parameter_constraints_clauseözniteliklerde değil, bu kapsamda türleri oluşturmak için kullanılabilir.

Tüm parametreler ve tür parametreleri farklı adlara sahip olmalıdır.

15.6.2 Yöntem parametreleri

15.6.2.1 Genel

Varsa, bir yöntemin parametreleri yöntemin parameter_list tarafından bildirilir.

parameter_list
    : fixed_parameters
    | fixed_parameters ',' parameter_array
    | parameter_array
    ;

fixed_parameters
    : fixed_parameter (',' fixed_parameter)*
    ;

fixed_parameter
    : attributes? parameter_modifier? type identifier default_argument?
    ;

default_argument
    : '=' expression
    ;

parameter_modifier
    : parameter_mode_modifier
    | 'this' parameter_mode_modifier?
    | parameter_mode_modifier? 'this'
    ;

parameter_mode_modifier
    : 'ref'
    | 'out'
    | 'in'
    ;

parameter_array
    : attributes? 'params' array_type identifier
    ;

Parametre listesi, yalnızca sonuncusu parameter_array olabilecek bir veya daha fazla virgülle ayrılmış parametreden oluşur.

fixed_parameter isteğe bağlı bir öznitelik kümesinden (§23) oluşur; isteğe bağlı in, out, refveya this değiştirici; tür; tanımlayıcı ve isteğe bağlı default_argument. Her fixed_parameter , verilen adla verilen türün bir parametresini bildirir. Değiştirici, this yöntemini bir uzantı yöntemi olarak tanımlar ve yalnızca genel olmayan, iç içe olmayan bir statik sınıftaki statik yöntemin ilk parametresinde izin verilir. Parametre bir struct türüyse veya bir struct türüyle kısıtlanmış bir tür parametresiyse, this değiştirici ref ya da in değiştiricisiyle birleştirilebilir, ancak out değiştiricisiyle birleştirilemez. Uzantı yöntemleri §15.6.10'da daha ayrıntılı olarak açıklanmıştır. Fixed_parameter ve bir default_argument'e sahip olanlar, isteğe bağlı parametre olarak bilinir, oysa fixed_parameter ve default_argument'e sahip olmayanlar gerekli parametredir. Gerekli bir parametre, parameter_list isteğe bağlı bir parametreden sonra gösterilmez.

ref, out veya this değiştiriciye sahip bir parametrenin varsayılan_argüman olamaz. Giriş parametresinin default_argument olabilir. ifade bir default_argument içinde aşağıdakilerden biri olmalıdır:

  • sabit_ifade
  • new S() bir değer türü olduğunda S biçimindeki ifade
  • default(S) bir değer türü olduğunda S biçimindeki ifade

İfade, kimlik veya null yapılabilirlik dönüşümüyle parametre türüne örtük olarak dönüştürülebilir olmalıdır.

İsteğe bağlı parametreler uygulama kısmi yöntem bildiriminde (§15.6.9), açık arabirim üyesi uygulamasında (§19.6.2), tek parametreli dizin oluşturucu bildiriminde (§15.9) veya işleç bildiriminde (§15.10.1) oluşursa, bu üyeler bağımsız değişkenlerin atlanması için hiçbir zaman çağrılamayacağı için derleyici uyarı vermelidir.

parameter_array isteğe bağlı bir öznitelik kümesi (§23), params değiştirici, array_type ve tanımlayıcıdan oluşur. Parametre dizisi, belirtilen dizi türünde belirtilen ada sahip tek bir parametreyi bildirir. Parametre dizisinin array_type tek boyutlu bir dizi türü (§17.2) olmalıdır. Yöntem çağrısında, parametre dizisi belirtilen dizi türünün tek bir bağımsız değişkeninin belirtilmesine izin verir veya dizi öğesi türünün sıfır veya daha fazla bağımsız değişkeninin belirtilmesine izin verir. Parametre dizileri §15.6.2.4 içinde daha ayrıntılı olarak açıklanmıştır.

İsteğe bağlı bir parametreden sonra bir parameter_array oluşabilir, ancak varsayılan bir değere sahip olamaz; bunun yerine bir parameter_array için bağımsız değişkenlerin atılması boş bir dizi oluşturulmasına neden olur.

Örnek: Aşağıda farklı parametre türleri gösterilmektedir:

void M<T>(
    ref int i,
    decimal d,
    bool b = false,
    bool? n = false,
    string s = "Hello",
    object o = null,
    T t = default(T),
    params int[] a
) { }

parameter_list için, M gerekli bir parametredir, i gerekli bir değer parametresidir, ref, d, b ve s isteğe bağlı değer parametreleridir ve o bir parametre dizisidir.

son örnek

Yöntem bildirimi, parametreler ve tür parametreleri için ayrı bir bildirim alanı (§7.3) oluşturur. Adlar, tür parametre listesi ve yöntemin parametre listesi tarafından bu bildirim alanına eklenir. Yöntemin gövdesi, varsa, bu bildirim alanı içinde yer aldığı kabul edilir. Yöntem bildirim alanında iki üyenin aynı ada sahip olması bir hatadır.

Yöntem çağrısı (§12.8.10.2), yöntemin parametrelerinin ve yerel değişkenlerinin bu çağrıya özgü bir kopyasını oluşturur ve çağrının bağımsız değişken listesi yeni oluşturulan parametrelere değerler veya değişken başvuruları atar. Bir yöntemin bloğu içinde parametrelere simple_name ifadelerdeki tanımlayıcıları tarafından başvurulabilir (§12.8.4).

Aşağıdaki parametre türleri vardır:

Not: §7.6'da açıklandığı gibi, in, outve ref değiştiricileri bir yöntemin imzasının parçasıdır, ancak params değiştirici değildir. son not

15.6.2.2 Değer parametreleri

Değiştirici olmadan bildirilen bir parametre bir değer parametresidir. Değer parametresi, ilk değerini yöntem çağırmada sağlanan karşılık gelen bağımsız değişkenden alan yerel bir değişkendir.

Kesin atama kuralları için bkz . §9.2.5.

Yöntem çağrısında karşılık gelen bağımsız değişken, parametre türüne örtük olarak dönüştürülebilir (§10.2) bir ifade olmalıdır.

Bir yöntemin bir değer parametresine yeni değerler atamasına izin verilir. Bu tür atamalar yalnızca değer parametresiyle temsil edilen yerel depolama konumunu etkiler; yöntem çağırmada verilen gerçek bağımsız değişken üzerinde hiçbir etkisi yoktur.

15.6.2.3 Referansla belirtilen parametreler

15.6.2.3.1 Genel

Giriş, çıkış ve referans parametreleri, başvuruya göre parametrelerdir. Başvuruya göre parametre yerel bir başvuru değişkenidir (§9.7); ilk başvuru, yöntem çağırmada sağlanan karşılık gelen bağımsız değişkenden alınır.

Not: Bir başvuru ile aktarılan parametrenin referansı, ref atama (= ref) operatörü kullanılarak değiştirilebilir.

Bir parametre başvuru parametresi olduğunda, yöntem çağrısındaki karşılık gelen bağımsız değişken, ilgili anahtar sözcük in, ref, veya out ile aynı türde bir variable_reference (§9.5) içermelidir. Ancak parametre bir in parametre olduğunda bağımsız değişken, ilgili parametrenin türüne ilişkin bağımsız değişken ifadesinden örtük dönüştürmenin (§10.2) bulunduğu bir ifade olabilir.

Yineleyici (§15.15) veya zaman uyumsuz işlev (§15.14) olarak bildirilen işlevlerde başvuru parametrelerine izin verilmez.

Birden çok başvuru parametresi alan bir yöntemde, birden çok adın aynı depolama konumunu temsil etmek mümkündür.

15.6.2.3.2 Giriş parametreleri

Değiştirici ile bildirilen parametre in bir giriş parametresidir. Giriş parametresine karşılık gelen bağımsız değişken, yöntem çağrısı noktasında var olan bir değişken veya yöntem çağırmasında uygulama (§12.6.2.3) tarafından oluşturulan değişkendir. Kesin atama kuralları için bkz . §9.2.8.

Bir giriş parametresinin değerini değiştirmek bir derleme zamanı hatasıdır.

Not: Giriş parametrelerinin birincil amacı verimliliktir. Yöntem parametresinin türü büyük bir yapı olduğunda (bellek gereksinimleri açısından), yöntemi çağırırken bağımsız değişkenin tüm değerini kopyalamaktan kaçınmak yararlı olur. Giriş parametreleri, yöntemlerin bellekteki mevcut değerlere başvurmasına olanak sağlarken, bu değerlerde istenmeyen değişikliklere karşı koruma sağlar. son not

15.6.2.3.3 Başvuru parametreleri

Değiştirici ile bildirilen parametre ref bir başvuru parametresidir. Kesin atama kuralları için bkz . §9.2.6.

Örnek: Örnek

class Test
{
    static void Swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }

    static void Main()
    {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine($"i = {i}, j = {j}");
    }
}

çıktıyı üretir

i = 2, j = 1

Swap öğesinin Main içindeki çağrısı için, xi'yi ve yj'yi temsil eder. Bu nedenle, çağrı i ve j değerlerini değiştirme etkisine sahiptir.

son örnek

Örnek: Aşağıdaki kodda

class A
{
    string s;
    void F(ref string a, ref string b)
    {
        s = "One";
        a = "Two";
        b = "Three";
    }

    void G()
    {
        F(ref s, ref s);
    }
}

F içindeki G çağrısı, hem s hem de a için b öğesine bir başvuru iletir. Bu nedenle, bu çağrı için , sve a adları baynı depolama konumuna başvurur ve üç atamanın tümü örnek alanını sdeğiştirir.

son örnek

Bir struct türü için, bir örnek metodunda, örnek erişimcisinde (§12.2.1) veya başlatıcı ile birlikte bir örnek oluşturucusunda, this anahtar sözcüğü, yapı türünün (§12.8.14) referans parametresi olarak tam olarak davranır.

15.6.2.3.4 Çıkış parametreleri

Değiştirici ile bildirilen parametre out bir çıkış parametresidir. Kesin atama kuralları için bkz . §9.2.7.

Kısmi yöntem olarak bildirilen bir yöntemin (§15.6.9) çıkış parametreleri olmamalıdır.

Not: Çıkış parametreleri genellikle birden çok dönüş değeri üreten yöntemlerde kullanılır. son not

Örnek:

class Test
{
    static void SplitPath(string path, out string dir, out string name)
    {
        int i = path.Length;
        while (i > 0)
        {
            char ch = path[i - 1];
            if (ch == '\\' || ch == '/' || ch == ':')
            {
                break;
            }
            i--;
        }
        dir = path.Substring(0, i);
        name = path.Substring(i);
    }

    static void Main()
    {
        string dir, name;
        SplitPath(@"c:\Windows\System\hello.txt", out dir, out name);
        Console.WriteLine(dir);
        Console.WriteLine(name);
    }
}

Örnek çıkışı oluşturur:

c:\Windows\System\
hello.txt

Unutmayın ki dir ve name değişkenleri SplitPath'ye geçirilmeden önce atanmayabilir ve çağrı sonrasında kesinlikle atanmış olarak kabul edilir.

son örnek

15.6.2.4 Parametre dizileri

Değiştirici ile bildirilen parametre params bir parametre dizisidir. Parametre listesi bir parametre dizisi içeriyorsa, listedeki son parametre ve tek boyutlu dizi türünde olmalıdır.

Örnek: ve string[] türleri string[][] parametre dizisinin türü olarak kullanılabilir, ancak türü string[,] kullanılamaz. son örnek

Not: Değiştiriciyi params , inveya outdeğiştiricileriyle refbirleştirmek mümkün değildir. son not

Parametre dizisi, yöntem çağırmada bağımsız değişkenlerin iki yoldan biriyle belirtilmesine izin verir:

  • Parametre dizisi için verilen bağımsız değişken, parametre dizisi türüne örtük olarak dönüştürülebilir (§10.2) tek bir ifade olabilir. Bu durumda, parametre dizisi tam olarak bir değer parametresi gibi davranır.
  • Alternatif olarak, çağırma parametre dizisi için sıfır veya daha fazla bağımsız değişken belirtebilir; burada her bağımsız değişken, parametre dizisinin öğe türüne örtük olarak dönüştürülebilir (§10.2) bir ifadedir. Bu durumda çağrı, parametre dizisi türünün bağımsız değişken sayısına karşılık gelen uzunluğu olan bir örneği oluşturur, verilen bağımsız değişken değerleriyle dizi örneğinin öğelerini başlatır ve yeni oluşturulan dizi örneğini gerçek bağımsız değişken olarak kullanır.

Bir çağrıda değişken sayıda bağımsız değişkene izin vermek dışında, parametre dizisi tam olarak aynı türde bir değer parametresine (§15.6.2.2) eşdeğerdir.

Örnek: Örnek

class Test
{
    static void F(params int[] args)
    {
        Console.Write($"Array contains {args.Length} elements:");
        foreach (int i in args)
        {
            Console.Write($" {i}");
        }
        Console.WriteLine();
    }

    static void Main()
    {
        int[] arr = {1, 2, 3};
        F(arr);
        F(10, 20, 30, 40);
        F();
    }
}

çıktıyı üretir

Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:

F'ın ilk çağrısı, diziyi bir değer parametresi olarak arr'ye geçirir. F'nin ikinci çağrısı, belirtilen öğe değerleriyle otomatik olarak dört öğeli int[] bir öğe oluşturur ve bu dizi örneğini bir değer parametresi olarak geçirir. Benzer şekilde, F üçüncü kez çağrıldığında sıfır-elemanlı bir int[] oluşturur ve bu örneği bir değer parametresi olarak geçirir. İkinci ve üçüncü çağrılar, yazma işlemiyle tam olarak eşdeğerdir:

F(new int[] {10, 20, 30, 40});
F(new int[] {});

son örnek

Aşırı yükleme çözümlemesi yaparken, parametre dizisine sahip bir yöntem normal biçiminde veya genişletilmiş biçiminde (§12.6.4.2) uygulanabilir. Bir yöntemin genişletilmiş biçimi yalnızca yöntemin normal biçimi geçerli değilse ve yalnızca genişletilmiş formla aynı imzaya sahip geçerli bir yöntem aynı türde önceden bildirilmemişse kullanılabilir.

Örnek: Örnek

class Test
{
    static void F(params object[] a) =>
        Console.WriteLine("F(object[])");

    static void F() =>
        Console.WriteLine("F()");

    static void F(object a0, object a1) =>
        Console.WriteLine("F(object,object)");

    static void Main()
    {
        F();
        F(1);
        F(1, 2);
        F(1, 2, 3);
        F(1, 2, 3, 4);
    }
}

çıktıyı üretir

F()
F(object[])
F(object,object)
F(object[])
F(object[])

Örnekte, parametre dizisine sahip yöntemin olası genişletilmiş biçimlerinden ikisi zaten normal yöntemler olarak sınıfına dahil edilir. Bu nedenle, bu genişletilmiş formlar aşırı yükleme çözümlemesi gerçekleştirilirken dikkate alınmaz ve birinci ve üçüncü yöntem çağrıları normal yöntemleri seçer. Bir sınıf parametre dizisine sahip bir yöntem bildirdiğinde, genişletilmiş formlardan bazılarını normal yöntemler olarak eklemek yaygındır. Bunu yaptığınızda, parametre dizisine sahip bir yöntemin genişletilmiş bir biçimi çağrıldığında oluşan bir dizi örneğinin ayrılmasını önlemek mümkündür.

son örnek

Dizi bir başvuru türüdür, bu nedenle parametre dizisi için geçirilen değer olabilir null.

Örnek: Örnek:

class Test
{
    static void F(params string[] array) =>
        Console.WriteLine(array == null);

    static void Main()
    {
        F(null);
        F((string) null);
    }
}

çıktıyı üretir:

True
False

İkinci çağırma, tek bir null başvuru içeren bir diziyi geçirdiğinden False üretir, çünkü F(new string[] { null }) ile eşdeğerdir.

son örnek

Parametre dizisinin türü olduğunda object[], yöntemin normal biçimi ile tek object bir parametre için genişletilmiş form arasında olası bir belirsizlik ortaya çıkar. Belirsizliğin nedeni, bir object[]'nin kendisinin örtük olarak object türüne dönüştürülebilir olmasıdır. Ancak, gerekirse bir atama eklenerek çözümlenebileceği için belirsizlik hiçbir sorun sunmaz.

Örnek: Örnek

class Test
{
    static void F(params object[] args)
    {
        foreach (object o in args)
        {
            Console.Write(o.GetType().FullName);
            Console.Write(" ");
        }
        Console.WriteLine();
    }

    static void Main()
    {
        object[] a = {1, "Hello", 123.456};
        object o = a;
        F(a);
        F((object)a);
        F(o);
        F((object[])o);
    }
}

çıktıyı üretir

System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double

F'in ilk ve son çağrılarında, normal biçimi F geçerlidir çünkü bağımsız değişken türünden parametre türüne, her ikisi de object[] türünde olduğu için, örtük bir dönüştürme vardır. Bu nedenle, aşırı yükleme çözümlemesi normal biçimini F seçer ve bağımsız değişken normal değer parametresi olarak geçirilir. İkinci ve üçüncü çağrılarda, bağımsız değişken türünden parametre türüne örtük dönüştürme olmadığından normal biçimi F geçerli değildir (tür object örtük olarak türe object[]dönüştürülemez). Ancak genişletilmiş biçimi F uygulanabilir, bu nedenle aşırı yük çözümlemesi ile seçilir. Sonuç olarak, çağrının bir parçası olarak tek öğeden oluşan bir object[] oluşturulur ve dizinin bu tek öğesi, verilen bağımsız değişken değeriyle başlatılır (ki bu da bir object[] başvurusudur).

son örnek

15.6.3 Statik ve örnek yöntemleri

Yöntem bildirimi bir static değiştirici içerdiğinde, bu yöntemin statik bir yöntem olduğu söylenir. Değiştirici olmadığında static yönteminin bir örnek yöntemi olduğu söylenir.

Statik yöntem belirli bir örnek üzerinde çalışmaz ve bir statik yöntemde this referans vermek derleme zamanı hatasıdır.

Örnek metodu, bir sınıfın belirli bir örneği üzerinde çalışır ve bu örneğe this olarak §12.8.14 erişilebilir.

Statik ve örnek üyeleri arasındaki farklar §15.3.8'de daha ayrıntılı olarak ele alınıyor.

15.6.4 Sanal yöntemler

Örnek yöntemi bildirimi bir sanal değiştirici içerdiğinde, bu yöntemin bir sanal yöntem olduğu söylenir. Sanal değiştirici olmadığında, yönteminin sanal olmayan bir yöntem olduğu söylenir.

Sanal olmayan bir yöntemin uygulanması sabittir: Yöntemin bildirildiği sınıfın bir örneğinde veya türetilmiş bir sınıfın örneğinde çağrılırsa, uygulama aynıdır. Buna karşılık, sanal yöntemin uygulanması türetilmiş sınıflar tarafından değiştirilebilir. Devralınan bir sanal yöntemin uygulanmasının yerini alma işlemi, bu yöntemi geçersiz kılma olarak bilinir (§15.6.5).

Sanal yöntem çağrısında, çağrının gerçekleştiği örneğin çalışma zamanı türü , çağrılacak gerçek yöntem uygulamasını belirler. Sanal olmayan bir yöntem çağrısında, örneğin derleme zamanı türü belirleyici faktördür. Tam olarak, bir N adlı yöntem, derleme zamanı türü A ve çalışma zamanı türü C olan bir örnekte, R ya da R'dan türetilmiş bir sınıf olarak, bağımsız değişken listesi C ile çağrıldığında, çağrı şu şekilde işlenir:

  • Bağlama zamanında aşırı yükleme çözümlemesi, içinde bildirilen ve tarafından Cdevralınan yöntem kümesinden belirli bir yöntemi N seçmek için , Ave öğesine uygulanırMC. Bu, §12.8.10.2'de açıklanmıştır.
  • Ardından çalışma zamanında:
    • Sanal olmayan bir yöntemse MM çağrılır.
    • Aksi takdirde, M sanal bir yöntemdir ve ile ilgili M olarak en türetilmiş uygulaması R çağrılır.

Içinde bildirilen veya bir sınıf tarafından devralınan her sanal yöntem için yöntemin bu sınıfa göre en türetilmiş bir uygulaması vardır. Bir sanal yöntemin M bir sınıfa R göre en çok türetilmiş uygulaması aşağıdaki gibi belirlenir:

  • R öğesinin tanıtıcı sanal bildirimini M içeriyorsa, bu M bağlamında R olarak en türetilmiş uygulamadır.
  • Aksi takdirde, RM'in bir geçersiz kılmasını içeriyorsa, bu M'e göre R olarak en türetilmiş uygulamasıdır.
  • Aksi takdirde, M ile ilgili R'ün en türetilmiş uygulaması, M'ün doğrudan temel sınıfı ile ilgili R'nün en türetilmiş uygulamasıyla aynıdır.

Örnek: Aşağıdaki örnek, sanal ve sanal olmayan yöntemler arasındaki farkları gösterir:

class A
{
    public void F() => Console.WriteLine("A.F");
    public virtual void G() => Console.WriteLine("A.G");
}

class B : A
{
    public new void F() => Console.WriteLine("B.F");
    public override void G() => Console.WriteLine("B.G");
}

class Test
{
    static void Main()
    {
        B b = new B();
        A a = b;
        a.F();
        b.F();
        a.G();
        b.G();
    }
}

Örnekte, A sanal olmayan bir yöntem F ve bir sanal yöntem Gtanıtır. sınıfı yeni bir sanal olmayan yöntem Btanıtır, bu nedenle ve devralınan yöntemini Fgeçersiz .F Örnek çıkışı oluşturur:

A.F
B.F
B.G
B.G

a.G() değil B.G, A.G çağırdığını fark edin. Bunun nedeni, örneğin derleme zamanı türü (yani B) değil, çalışma zamanı türü (yani A) çağrılacak gerçek yöntem uygulamasını belirler.

son örnek

Yöntemlerin devralınan yöntemleri gizlemesine izin verildiğinden, sınıfın aynı imzaya sahip birkaç sanal yöntem içermesi mümkündür. En çok türetilen yöntem dışında tümü gizlendiğinden, bu bir belirsizlik sorunu sunmaz.

Örnek: Aşağıdaki kodda

class A
{
    public virtual void F() => Console.WriteLine("A.F");
}

class B : A
{
    public override void F() => Console.WriteLine("B.F");
}

class C : B
{
    public new virtual void F() => Console.WriteLine("C.F");
}

class D : C
{
    public override void F() => Console.WriteLine("D.F");
}

class Test
{
    static void Main()
    {
        D d = new D();
        A a = d;
        B b = d;
        C c = d;
        a.F();
        b.F();
        c.F();
        d.F();
    }
}

C ve D sınıfları aynı imzaya sahip iki sanal yöntem içerir: tarafından A tanıtılan ve tarafından Ctanıtılan yöntem. C tarafından sunulan yöntem, A'dan devralınan yöntemi gizler. Bu nedenle, D içindeki geçersiz kılma bildirimi, C tarafından tanıtılmış yöntemi geçersiz kılar, ve D tarafından tanıtılmış yöntemi A tarafından geçersiz kılmak mümkün değildir. Örnek çıkışı oluşturur:

B.F
B.F
D.F
D.F

Gizli sanal yöntemi, yöntemin gizli olmadığı daha az türetilmiş bir tür aracılığıyla D örneğine erişerek çağırmanın mümkün olduğunu unutmayın.

son örnek

15.6.5 Geçersiz kılma yöntemleri

Örnek yöntemi bildirimi bir override değiştirici içeriyorsa, bu yöntem bir aşırı yükleme yöntemi olarak adlandırılır. Geçersiz kılma yöntemi, aynı imzaya sahip devralınan bir sanal yöntemi geçersiz kılar. Sanal yöntem bildirimi yeni bir yöntem tanıtırken , geçersiz kılma yöntemi bildirimi bu yöntemin yeni bir uygulamasını sağlayarak var olan devralınan bir sanal yöntemi özel kılar.

Gecersiz kılma bildirimi ile geçersiz kılınan yöntem, "gecersiz kılınan temel yöntem" olarak bilinir. Bir sınıfta bildirilen geçersiz kılma yöntemi için, geçersiz kılınan temel yöntemi belirlemek amacıyla, M'nin doğrudan temel sınıfı olan C'dan başlayarak her ardışık doğrudan temel sınıfın incelenmesiyle, tür bağımsız değişkenlerinin yerine konması işlemi sonrasında C ile aynı imzaya sahip erişilebilir en az bir yöntem bulunan yere kadar devam edilir. Geçersiz kılınan temel yöntemi bulmak amacıyla, bir yöntem şayet public ise, protected ise, protected internal ise ya da internal veya private protected olup C ile aynı programda bildirilirse erişilebilir olarak kabul edilir.

Geçersiz kılma yöntemi, geçersiz kılınan temel yöntemin type_parameter_constraints_clause'lerinidevralır.

Geçersiz kılma bildirimi için aşağıdakilerin tümü doğru olmadığı sürece derleme zamanı hatası oluşur:

  • Geçersiz kılınan bir temel yöntem yukarıda açıklandığı gibi bulunabilir.
  • Tam olarak geçersiz kılınan bir temel yöntem vardır. Bu kısıtlama yalnızca temel sınıf türü, tür bağımsız değişkenlerinin değiştirilmesinin iki yöntemin imzasını aynı hale getirdiği oluşturulmuş bir tür olduğunda geçerlidir.
  • Geçersiz kılınan temel yöntem sanal, soyut veya geçersiz kılma yöntemidir. Başka bir deyişle, geçersiz kılınan temel yöntem statik veya sanal olmayan olamaz.
  • Geçersiz kılınan temel yöntem mühürlü bir yöntem değildir.
  • Geçersiz kılınan temel yöntemin dönüş türü ile geçersiz kılma yöntemi arasında bir kimlik dönüştürmesi vardır.
  • Geçersiz kılma bildirimi ve geçersiz kılınan temel yöntem aynı bildirilen erişilebilirliği içerir. Başka bir deyişle, geçersiz kılma bildirimi sanal yöntemin erişilebilirliğini değiştiremez. Ancak geçersiz kılınan temel yöntem dahili olarak korunuyorsa ve geçersiz kılma bildirimini içeren derlemeden farklı bir derlemede bildiriliyorsa geçersiz kılma bildiriminin bildirilen erişilebilirliği korunmalıdır.
  • type_parameter_constraints_clause, yalnızca class veya structprimary_constraint içerebilir; bunlar, devralınan kısıtlamalara göre, sırasıyla başvuru veya değer türleri olarak bilinen type_parameter üzerine uygulanır. Geçersiz kılma yönteminin imzasında T? yer alan ve tür parametresi olan herhangi bir form T türü aşağıdaki gibi yorumlanır:
    • Tür parametresi class için bir T kısıtlama eklenirse T? nullable bir başvuru türüdür; aksi takdirde
    • Tür parametresi struct için ya herhangi bir kısıtlama eklenmemişse ya da bir T kısıtlama eklenirse, T? null atanabilir bir değer türü olur.

Örnek: Aşağıda geçersiz kılma kurallarının genel sınıflar için nasıl çalıştığı gösterilmektedir:

abstract class C<T>
{
    public virtual T F() {...}
    public virtual C<T> G() {...}
    public virtual void H(C<T> x) {...}
}

class D : C<string>
{
    public override string F() {...}            // Ok
    public override C<string> G() {...}         // Ok
    public override void H(C<T> x) {...}        // Error, should be C<string>
}

class E<T,U> : C<U>
{
    public override U F() {...}                 // Ok
    public override C<U> G() {...}              // Ok
    public override void H(C<T> x) {...}        // Error, should be C<U>
}

son örnek

Örnek: Aşağıda, tür parametreleri söz konusu olduğunda geçersiz kılma kurallarının nasıl çalıştığı gösterilmektedir:

#nullable enable
class A
{
    public virtual void Foo<T>(T? value) where T : class { }
    public virtual void Foo<T>(T? value) where T : struct { }
}
class B: A
{
    public override void Foo<T>(T? value) where T : class { }
    public override void Foo<T>(T? value) where T : struct { }
}

Tür parametresi kısıtlaması where T : classolmadan, başvuru türü belirlenmiş tür parametresine sahip temel yöntem geçersiz kılınamaz. son örnek

Geçersiz kılma bildirimi, geçersiz kılınan temel yönteme bir base_access (§12.8.15) kullanarak erişebilir.

Örnek: Aşağıdaki kodda

class A
{
    int x;

    public virtual void PrintFields() => Console.WriteLine($"x = {x}");
}

class B : A
{
    int y;

    public override void PrintFields()
    {
        base.PrintFields();
        Console.WriteLine($"y = {y}");
    }
}

base.PrintFields() içindeki çağırma, içinde BAbildirilen PrintFields yöntemini çağırır. base_access, sanal çağırma mekanizmasını devre dışı bırakır ve temel yöntemi yöntem olmayanvirtual bir yöntem olarak ele alır. çağrısı B yazılsaydı((A)this).PrintFields(), içinde bildirilen PrintFieldsyöntemi değil, içinde bildirilen BA yöntemi yinelemeli olarak çağırır PrintFields çünkü sanal ve çalışma zamanı türü ((A)this) olurB.

son örnek

Yalnızca bir override değiştirici ekleyerek bir yöntem başka bir yöntemi geçersiz kılabilir. Diğer tüm durumlarda, devralınan yöntemle aynı imzaya sahip bir yöntem yalnızca devralınan yöntemi gizler.

Örnek: Aşağıdaki kodda

class A
{
    public virtual void F() {}
}

class B : A
{
    public virtual void F() {} // Warning, hiding inherited F()
}

F yöntemi, B içinde bir override değiştirici içermez ve bu nedenle F içindeki A yönteminin üstüne yazmaz. Bunun yerine içindeki F yöntemi B içindeki Ayöntemini gizler ve bildirimi yeni bir değiştirici içermediğinden bir uyarı bildirilir.

son örnek

Örnek: Aşağıdaki kodda

class A
{
    public virtual void F() {}
}

class B : A
{
    private new void F() {} // Hides A.F within body of B
}

class C : B
{
    public override void F() {} // Ok, overrides A.F
}

F yöntemindeki B, F'den devralınan sanal A yöntemini gizler. Yeni F, B içinde özel erişime sahip olduğundan, kapsamı yalnızca B sınıfının gövdesini içerir ve C ile genişletilmez. Bu nedenle, içinde F bildiriminin C öğesinden Fdevralınan öğesini A geçersiz kılmasına izin verilir.

son örnek

15.6.6 Korumalı yöntemler

Bir örnek yöntemi bildirimi bir sealed değiştirici içerdiğinde, bu yöntemin korumalı bir yöntem olduğu söylenir. Korumalı yöntem, aynı imzaya sahip devralınan bir sanal yöntemi geçersiz kılar. Mühürlü bir yöntem de override değiştirici ile işaretlenmelidir. Değiştiricinin sealed kullanılması, türetilmiş bir sınıfın yöntemini daha fazla geçersiz kılmasını engeller.

Örnek: Örnek

class A
{
    public virtual void F() => Console.WriteLine("A.F");
    public virtual void G() => Console.WriteLine("A.G");
}

class B : A
{
    public sealed override void F() => Console.WriteLine("B.F");
    public override void G()        => Console.WriteLine("B.G");
}

class C : B
{
    public override void G() => Console.WriteLine("C.G");
}

sınıfı B iki geçersiz kılma yöntemi sağlar: değiştiricisi F olan bir sealed yöntem ve olmayan bir G yöntem. B'nin sealed değiştiricisini kullanması, C'nın F'yi daha fazla geçersiz kılmasını engeller.

son örnek

15.6.7 Soyut yöntemler

Örnek yöntemi bildirimi bir abstract değiştirici içerdiğinde, bu yöntemin soyut bir yöntem olduğu söylenir. Soyut bir yöntem örtük olarak da sanal bir yöntem olsa da değiştiricisine virtualsahip olamaz.

Soyut yöntem bildirimi yeni bir sanal yöntem tanıtır, ancak bu yöntemin bir uygulamasını sağlamaz. Bunun yerine, soyut olmayan türetilmiş sınıfların bu yöntemi geçersiz kılarak kendi uygulamalarını sağlaması gerekir. Soyut bir yöntemin method_body yalnızca noktalı virgülden oluşur.

Soyut yöntem bildirimlerine yalnızca soyut sınıflarda (§15.2.2.2) ve arabirimlerde (§19.4.3) izin verilir.

Örnek: Aşağıdaki kodda

public abstract class Shape
{
    public abstract void Paint(Graphics g, Rectangle r);
}

public class Ellipse : Shape
{
    public override void Paint(Graphics g, Rectangle r) => g.DrawEllipse(r);
}

public class Box : Shape
{
    public override void Paint(Graphics g, Rectangle r) => g.DrawRect(r);
}

Shape sınıfı, kendisini boyayabilen geometrik şekil nesnesinin soyut tanımını tanımlar. Şeklin Paint soyut kavramı için anlamlı bir geri dönüş uygulaması olmadığından yöntemi soyut olur. Ellipse ve Box sınıfları somut Shape uygulamalardır. Soyut olmayan bu sınıflar, Paint yöntemini geçersiz kılmalı ve gerçek bir uygulama sağlamalıdır.

son örnek

base_access (§12.8.15) bir soyut yönteme başvurduğunda, bu bir derleme zamanı hatasıdır.

Örnek: Aşağıdaki kodda

abstract class A
{
    public abstract void F();
}

class B : A
{
    // Error, base.F is abstract
    public override void F() => base.F();
}

base.F() çağrısı için, soyut bir yönteme başvurduğu için derleme zamanı hatası bildirilir.

son örnek

Soyut yöntem bildiriminin bir sanal yöntemi geçersiz kılmasına izin verilir. Bu, soyut bir sınıfın türetilmiş sınıflarda yöntemin yeniden uygulanmasını zorlamasına olanak tanır ve yöntemin özgün uygulamasını kullanılamaz hale getirir.

Örnek: Aşağıdaki kodda

class A
{
    public virtual void F() => Console.WriteLine("A.F");
}

abstract class B: A
{
    public abstract override void F();
}

class C : B
{
    public override void F() => Console.WriteLine("C.F");
}

sınıfı A bir sanal yöntem bildirir, sınıf B bu yöntemi soyut bir yöntemle geçersiz kılar ve sınıf C kendi uygulamasını sağlamak için soyut yöntemi geçersiz kılar.

son örnek

15.6.8 Dış yöntemler

Yöntem bildirimi bir extern değiştirici içerdiğinde, yöntemin bir dış yöntem olduğu söylenir. Dış yöntemler genellikle C# dışında bir dil kullanılarak harici olarak uygulanır. Dış yöntem bildirimi gerçek bir uygulama sağlamadığından, bir dış yöntemin yöntem gövdesi yalnızca noktalı virgülden oluşur. Dış yöntem genel olmamalıdır.

Bir dış yönteme bağlantı elde edilen mekanizma, uygulama tanımlıdır.

Örnek: Aşağıdaki örnekte değiştirici ve özniteliğinin extern kullanımı gösterilmektedir DllImport :

class Path
{
    [DllImport("kernel32", SetLastError=true)]
    static extern bool CreateDirectory(string name, SecurityAttribute sa);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool RemoveDirectory(string name);

    [DllImport("kernel32", SetLastError=true)]
    static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool SetCurrentDirectory(string name);
}

son örnek

15.6.9 Kısmi yöntemler

Yöntem bildirimi bir partial değiştirici içerdiğinde, bu yöntemin kısmi bir yöntem olduğu söylenir. Kısmi yöntemler yalnızca kısmi türlerin (§15.2.7) üyesi olarak bildirilebilir ve bir dizi kısıtlamaya tabidir.

Kısmi yöntemler bir tür bildiriminin bir bölümünde tanımlanabilir ve başka bir türde uygulanabilir. Uygulama isteğe bağlıdır; Kısmi yöntemi hiçbir bölüm uygulamazsa, kısmi yöntem bildirimi ve ona yapılan tüm çağrılar, parçaların birleşiminden kaynaklanan tür bildiriminden kaldırılır.

Kısmi yöntemler erişim değiştiricileri tanımlamaz; örtük olarak özeldirler. Dönüş türleri voidolacaktır ve parametreleri çıkış parametreleri olmayacaktır. Tanımlayıcı partial , bir yöntem bildiriminde bağlamsal anahtar sözcük (§6.4.4) olarak kabul edilir, ancak anahtar sözcüğün void hemen önünde görünürse. Kısmi bir yöntem, arabirim yöntemlerini açıkça uygulayamaz.

İki tür kısmi yöntem bildirimi vardır: Yöntem bildiriminin gövdesi noktalı virgül ise, bildirimin tanımlayıcı bir kısmi yöntem bildirimi olduğu söylenir. Gövde noktalı virgülden farklıysa, bildirimin uygulayan bir kısmi yöntem bildirimi olduğu söylenir. Bir tür bildiriminin bölümleri arasında, belirli bir imzaya sahip kısmi yöntem bildirimini tanımlayan tek bir tane olmalı ve belirli bir imzaya sahip kısmi yöntem bildirimini uygulayan en az bir tane olmalıdır. Uygulanan kısmi yöntem bildirimi verilirse, karşılık gelen bir tanımlama kısmi yöntem bildirimi bulunacaktır ve beyanlar aşağıda belirtilen şekilde eşleşmelidir:

  • Bildirimler aynı değiştiricilere (aynı sırada olmasa da), yöntem adına, tür parametrelerinin sayısına ve parametre sayısına sahip olmalıdır.
  • Bildirimlerdeki karşılık gelen parametreler aynı değiştiricilere (aynı sırada olmasa da) ve aynı türlere veya kimlik dönüştürülebilir türlerine (tür parametre adlarındaki modül farklılıkları) sahip olmalıdır.
  • Bildirimlerdeki karşılık gelen tür parametreleri aynı kısıtlamalara (tür parametre adlarındaki modül farklılıkları) sahip olmalıdır.

Uygulanan kısmi yöntem bildirimi, tanımlayıcı kısmi yöntem bildirimi ile aynı bölümde görünebilir.

Aşırı yükleme çözümlemeye yalnızca tanımlayıcı bir kısmi yöntem katılır. Bu nedenle, bir uygulama bildirimi verilip verilmediğine bakılmaksızın, çağrı ifadeleri kısmi yöntemin çağrılarına dönüşebilir. Kısmi bir yöntem her zaman void döndüreceğinden, bu tür çağırma kavramları her zaman ifade cümleleri olacaktır. Ayrıca, kısmi bir yöntem örtük privateolduğundan, bu tür deyimler her zaman içinde kısmi yöntemin bildirildiği tür bildiriminin bölümlerinden birinde gerçekleşir.

Not: Kısmi yöntem bildirimleri tanımlama ve uygulama eşleştirme tanımı, parametre adlarının eşleşmesini gerektirmez. Bu, iyi tanımlanmış olsa da, adlandırılmış bağımsız değişkenler (§12.6.2.1) kullanıldığında şaşırtıcı bir davranış oluşturabilir. Örneğin, bir dosyada tanımlanan M için kısmi yöntem bildirimi ve başka bir dosyada uygulanan kısmi yöntem bildirimi:

// File P1.cs:
partial class P
{
    static partial void M(int x);
}

// File P2.cs:
partial class P
{
    static void Caller() => M(y: 0);
    static partial void M(int y) {}
}

Geçersizdir çünkü çağrı, tanımlayıcı kısmi yöntem bildiriminden değil, uygulayıcıdan bağımsız değişken adını kullanmaktadır.

son not

Kısmi tür bildiriminin hiçbir parçası belirli bir kısmi yöntem için uygulama bildirimi içermiyorsa, onu çağıran ifade deyimleri birleştirilmiş tür bildiriminden kaldırılır. Bu nedenle, herhangi bir alt ifade de dahil olmak üzere çağırma ifadesinin çalışma zamanında hiçbir etkisi yoktur. Kısmi yöntemin kendisi de kaldırılır ve birleştirilmiş tür bildiriminin üyesi olmaz.

Belirli bir kısmi yöntem için uygulayan bir bildirim varsa, kısmi yöntemlerin çağrıları korunur. Kısmi yöntem, aşağıdakiler dışında uygulama kısmi yöntem bildirimine benzer bir yöntem bildirimine neden oluyor:

  • Değiştirici partial dahil değildir.

  • Sonuçta elde edilen yöntem bildirimindeki öznitelikler, tanımlamanın birleştirilmiş öznitelikleri ve belirtilmemiş sırada kısmi yöntem bildiriminin uygulanmasıdır. Kopyalar kaldırılmıyor.

  • Sonuçta elde edilen yöntem bildiriminin parametrelerindeki öznitelikler, tanımlamanın karşılık gelen parametrelerinin birleştirilmiş öznitelikleri ve belirtilmeyen sırada kısmi yöntem bildirimini uygulamadır. Kopyalar kaldırılmıyor.

Kısmi bir yöntem Miçin tanımlayıcı bir bildirim verilir ancak uygulama bildirimi verilmezse, aşağıdaki kısıtlamalar geçerlidir:

  • (M) içinden temsilci oluşturmak derleme zamanı hatasıdır.

  • Anonim bir fonksiyonun ifade ağacı türüne dönüştürüldüğü durumda, içine M atıfta bulunmak derleme zamanı hatasıdır (§8.6).

  • çağrısının M bir parçası olarak gerçekleşen ifadeler, kesin atama durumunu (§9.4) etkilemez ve bu da derleme zamanı hatalarına neden olabilir.

  • M bir uygulamanın giriş noktası olamaz (§7.1).

Kısmi yöntemler, bir tür bildiriminin bir bölümünün başka bir bölümün davranışını özelleştirmesine izin vermek için kullanışlıdır; örneğin, bir araç tarafından oluşturulan bir bölüm. Aşağıdaki kısmi sınıf bildirimini göz önünde bulundurun:

partial class Customer
{
    string name;

    public string Name
    {
        get => name;
        set
        {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }
    }

    partial void OnNameChanging(string newName);
    partial void OnNameChanged();
}

Bu sınıf başka bir bölüm olmadan derlenirse, kısmi yöntem bildirimlerini ve çağrılarını tanımlama kaldırılır ve sonuçta elde edilen birleştirilmiş sınıf bildirimi aşağıdakine eşdeğer olur:

class Customer
{
    string name;

    public string Name
    {
        get => name;
        set => name = value;
    }
}

Bununla birlikte, kısmi yöntemlerin bildirimlerini uygulamayı sağlayan başka bir bölümün verildiğini varsayalım:

partial class Customer
{
    partial void OnNameChanging(string newName) =>
        Console.WriteLine($"Changing {name} to {newName}");

    partial void OnNameChanged() =>
        Console.WriteLine($"Changed to {name}");
}

Ardından elde edilen birleştirilmiş sınıf bildirimi aşağıdakine eşdeğer olacaktır:

class Customer
{
    string name;

    public string Name
    {
        get => name;
        set
        {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }
    }

    void OnNameChanging(string newName) =>
        Console.WriteLine($"Changing {name} to {newName}");

    void OnNameChanged() =>
        Console.WriteLine($"Changed to {name}");
}

15.6.10 Uzantı yöntemleri

Bir yöntemin ilk parametresi değiştiriciyi içerdiğinde this , bu yöntemin bir uzantı yöntemi olduğu söylenir. Uzantı yöntemleri yalnızca genel olmayan, iç içe olmayan statik sınıflarda bildirilmelidir. Uzantı yönteminin ilk parametresi aşağıdaki gibi kısıtlanır:

  • Yalnızca bir değer türü varsa bir giriş parametresi olabilir.
  • Yalnızca bir değer türüne sahipse veya yapısı kısıtlanmış genel bir türe sahipse bir başvuru parametresi olabilir.
  • Bu bir çıkış parametresi olmamalıdır.
  • İşaretçi türü olmayacaktır.

Örnek: Aşağıda, iki uzantı yöntemi bildiren bir statik sınıf örneği verilmiştir:

public static class Extensions
{
    public static int ToInt32(this string s) => Int32.Parse(s);

    public static T[] Slice<T>(this T[] source, int index, int count)
    {
        if (index < 0 || count < 0 || source.Length - index < count)
        {
            throw new ArgumentException();
        }
        T[] result = new T[count];
        Array.Copy(source, index, result, 0, count);
        return result;
    }
}

son örnek

Uzantı yöntemi normal bir statik yöntemdir. Buna ek olarak, kapsayan statik sınıfı kapsam dahilinde olduğunda, ilk bağımsız değişken olarak alıcı ifadesi kullanılarak örnek yöntemi çağırma söz dizimi (§12.8.10.3) kullanılarak bir uzantı yöntemi çağrılabilir.

Örnek: Aşağıdaki program yukarıda bildirilen uzantı yöntemlerini kullanır:

static class Program
{
    static void Main()
    {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in strings.Slice(1, 2))
        {
            Console.WriteLine(s.ToInt32());
        }
    }
}

Slice yöntemi üzerinde string[]kullanılabilir ve ToInt32 uzantı yöntemleri olarak bildirildiği için yöntemi üzerinde stringkullanılabilir. Programın anlamı, sıradan statik yöntem çağrıları kullanılarak aşağıdakiyle aynıdır:

static class Program
{
    static void Main()
    {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in Extensions.Slice(strings, 1, 2))
        {
            Console.WriteLine(Extensions.ToInt32(s));
        }
    }
}

son örnek

15.6.11 Yöntem gövdesi

Yöntem bildiriminin yöntem gövdesi bir blok gövdesinden, ifade gövdesinden veya noktalı virgülden oluşur.

Soyut ve dış yöntem bildirimleri bir yöntem uygulaması sağlamaz, bu nedenle yöntem gövdeleri yalnızca noktalı virgülden oluşur. Diğer herhangi bir yöntem için, yöntem gövdesi, bu yöntem çağrıldığında yürütülecek deyimleri içeren bir blok (§13.3) şeklindedir.

Bir yöntemin geçerli dönüş türü, dönüş türü void ise ya da yöntem zaman uyumsuz olup dönüş türü void ise, «TaskType» olur (§15.14.1). Aksi takdirde, zaman uyumsuz bir yöntemin etkin dönüş türü dönüş türüdür ve dönüş türüne («TaskType»<T>) sahip bir zaman uyumsuz yöntemin etkin dönüş türü olurT.

Bir yöntemin etkin dönüş türü olduğunda void ve yöntemin bir blok gövdesi olduğunda, return bloktaki deyimler (§13.10.5) bir ifade belirtmez. Void yönteminin bloğunun yürütülmesi normal bir şekilde tamamlanırsa (yani, denetim yöntem gövdesinin sonundan akar), bu yöntem yalnızca çağırana geri döner.

Bir yöntemin etkili dönüş türü olduğunda void ve yöntemin bir ifade gövdesi olduğunda, ifade E bir statement_expression olmalıdır ve gövde formun { E; }bir blok gövdesine tam olarak eşdeğerdir.

Değere göre dönüş yöntemi (§15.6.1) için, bu yöntemin gövdesindeki her dönüş deyimi, etkin dönüş türüne örtük olarak dönüştürülebilir bir ifade belirtecektir.

Referansla dönen bir yöntem (§15.6.1) için, bu yöntemin gövdesindeki her dönüş deyimi, türü etkin dönüş türünde olan ve çağıran bağlamındaki ref güvenli bağlama sahip bir ifade belirtecektir (§9.7.2).

Değere göre dönüşler ve referansa göre dönüş yöntemleri için yöntem gövdesinin uç noktasına ulaşılmamalıdır. Başka bir deyişle, denetimin yöntem gövdesinin sonundan akışına izin verilmez.

Örnek: Aşağıdaki kodda

class A
{
    public int F() {} // Error, return value required

    public int G()
    {
        return 1;
    }

    public int H(bool b)
    {
        if (b)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    public int I(bool b) => b ? 1 : 0;
}

değer döndüren F yöntem, denetim yöntem gövdesinin sonundan akabildiği için derleme zamanı hatasıyla sonuçlanır. G ve H yöntemleri, tüm olası yürütme yolları bir dönüş değeri belirten bir return deyimiyle sona erdiğinden doğrudur. I yöntemi doğrudur çünkü gövdesi içinde yalnızca tek bir return deyimi olan bir blokla eşdeğerdir.

son örnek

15.7 Özellikleri

15.7.1 Genel

Özellik, bir nesnenin veya sınıfın özelliğine erişim sağlayan bir üyedir. Özelliklere örnek olarak dizenin uzunluğu, yazı tipi boyutu, pencerenin resim yazısı ve müşterinin adı verilebilir. Özellikler alanların doğal bir uzantısıdır; her ikisi de ilişkili türlere sahip adlandırılmış üyelerdir ve alanlara ve özelliklere erişmek için kullanılan söz dizimi aynıdır. Ancak, alanlardan farklı olarak, özellikler depolama konumlarını belirtmez. Bunun yerine, özelliklerin değerleri okunduğunda veya yazılırken yürütülecek deyimleri belirten erişimcilerivardır. Bu nedenle özellikler, eylemleri bir nesnenin veya sınıfın özelliklerinin okunması ve yazması ile ilişkilendirmeye yönelik bir mekanizma sağlar; ayrıca, bu özelliklerin hesaplanması için izin verir.

Özellikler property_declarationkullanılarak bildirilir:

property_declaration
    : attributes? property_modifier* type member_name property_body
    | attributes? property_modifier* ref_kind type member_name ref_property_body
    ;    

property_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'readonly'        // direct struct members only
    | unsafe_modifier   // unsafe code support
    ;
    
property_body
    : '{' accessor_declarations '}' property_initializer?
    | '=>' expression ';'
    ;

property_initializer
    : '=' variable_initializer ';'
    ;

ref_property_body
    : '{' ref_get_accessor_declaration '}'
    | '=>' 'ref' variable_reference ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

property_declaration bir öznitelik kümesi (§23) ve izin verilen bildirilen erişilebilirlik türlerinden herhangi birini (§15.3.6), new (§15.3.5), (static (virtual, §15.7.6), override (§15.6.5, §15.7.6), sealed (§15.6.6), abstract (§15.6.7, §15.7.6) ve extern (§15.6.8). Ayrıca, doğrudan bir struct_declaration içeren bir property_declarationreadonly değiştiriciyi içerebilir (§16.4.11).

  • İlki, başv değeri olmayan bir özellik bildirir. Türü type olan değer. Bu tür bir özellik okunabilir ve/veya yazılabilir olabilir.
  • İkincisi ref değerli bir özellik bildirir. Değeri, türünde bir değişken için olan bir variable_reference (readonly) değeridir. Bu tür bir özellik yalnızca okunabilen.

property_declaration bir öznitelik kümesi (§23) ve izin verilen bildirilen erişilebilirlik türlerinden herhangi birini (§15.3.6), new (§15.3.5), static (§15.7.2), (§15.6.4, virtual§15.7.6), (override, §15.7.6), (sealed), (abstract, §15.7.6) ve (extern) değiştiriciler.

Özellik bildirimleri, geçerli değiştirici birleşimleriyle ilgili olarak yöntem bildirimleriyle (§15.6) aynı kurallara tabidir.

member_name (§15.6.1) özelliğin adını belirtir. Özellik açık bir arabirim üyesi uygulaması olmadığı sürece, member_name yalnızca bir tanımlayıcıdır. Açık arabirim üyesi uygulaması için (§19.6.2), member_name bir interface_type ve ardından bir "." ve bir tanımlayıcıdan oluşur.

Bir mülkiyetin türü en az mülkiyetin kendisi kadar erişilebilir olmalıdır (§7.5.5).

property_body bir deyim gövdesinden veya ifade gövdesinden oluşabilir. Bir deyim gövdesinde, "accessor_declarations" ifadesi, "" ve "{" belirteçleri ile çevrelenmiş olmalı ve özelliğin erişimcilerini (}) bildirmelidir. Erişimciler, özelliğin okunması ve yazılmasıyla bağlantılı olan çalıştırılabilir ifadeleri belirtir.

bir property_body bir ifade gövdesinin => ardından bir ifade ve noktalı virgülden oluşan bir ifadeE gövdesi deyim gövdesiyle { get { return E; } }tam olarak eşdeğerdir ve bu nedenle yalnızca get erişimcisinin sonucunun tek bir ifade tarafından verildiği salt okunur özellikleri belirtmek için kullanılabilir.

property_initializer yalnızca otomatik olarak uygulanan bir özellik (§15.7.4) için verilebilir ve bu özelliklerin temel alan değerinin ifade tarafından verilen değerle başlatılmasına neden olur.

ref_property_body bir deyim gövdesinden veya ifade gövdesinden oluşabilir. Bir ifade gövdesinde, bir get_accessor_declaration özelliğin get erişimcisini (§15.7.3) tanımlar. Erişimci, özelliğin okunmasıyla ilişkili çalıştırılabilir ifadeleri belirtir.

ref_property_body'de, => ardından ref, bir variable_referenceV ve bir noktalı virgülle oluşan bir ifade gövdesi, { get { return ref V; } } bildirim gövdesiyle tam olarak eşdeğerdir.

Not: Özelliğe erişim söz dizimi bir alan için söz dizimi ile aynı olsa da, özellik değişken olarak sınıflandırılmamıştır. Bu nedenle, özellik ref değeri verilmediği ve bu nedenle değişken başvurusu (in) döndürmediği sürece bir özelliği , outveya ref bağımsız değişkeni olarak geçirmek mümkün değildir. son not

Bir özellik bildirimi bir extern değiştirici içerdiğinde, özelliğin bir dış özellik olduğu söylenir. Dış bir özellik bildirimi gerçek bir uygulama sağlamadığından, accessor_declarations içindeki her bir accessor_body bir noktalı virgül olmalıdır.

15.7.2 Statik ve örnek özellikleri

Özellik bildirimi bir static değiştirici içerdiğinde, özelliğin statik bir özellik olduğu söylenir. Değiştirici olmadığında static , özelliğin bir örnek özelliği olduğu söylenir.

Statik özellik belirli bir örnekle ilişkilendirilmemiştir ve statik özelliğin erişimcilerinde this'ya başvurmak derleme zamanı hatasıdır.

Bir sınıfın belirli bir örneğiyle ilişkili olan örnek özelliği, o özelliğin erişimcilerinde this (§12.8.14) şeklinde erişilebilir.

Statik ve örnek üyeleri arasındaki farklar §15.3.8'de daha ayrıntılı olarak ele alınıyor.

15.7.3 Aksesuar

Not: Bu alt kural hem özellikler (§15.7) hem de dizin oluşturucular (§15.9) için geçerlidir. Alt hüküm, dizin oluşturucular için okuma yaparken, özellikler açısından yazılır ve özellik/özellikler yerine dizin oluşturucu/dizin oluşturucuları kullanılır. §15.9.2'de verilen özellikler ile dizin oluşturucular arasındaki farklar listesine bakın. son not

Bir özelliğin accessor_declarations, o özelliği yazma ve/veya okuma ile ilişkili yürütülebilir deyimleri belirtir.

accessor_declarations
    : get_accessor_declaration set_accessor_declaration?
    | set_accessor_declaration get_accessor_declaration?
    ;

get_accessor_declaration
    : attributes? accessor_modifier? 'get' accessor_body
    ;

set_accessor_declaration
    : attributes? accessor_modifier? 'set' accessor_body
    ;

accessor_modifier
    : 'protected'
    | 'internal'
    | 'private'
    | 'protected' 'internal'
    | 'internal' 'protected'
    | 'protected' 'private'
    | 'private' 'protected'
    | 'readonly'        // direct struct members only
    ;

accessor_body
    : block
    | '=>' expression ';'
    | ';' 
    ;

ref_get_accessor_declaration
    : attributes? accessor_modifier? 'get' ref_accessor_body
    ;
    
ref_accessor_body
    : block
    | '=>' 'ref' variable_reference ';'
    | ';'
    ;

accessor_declarations bir get_accessor_declaration, bir set_accessor_declaration veya her ikisinden de oluşur. Her erişimci bildirimi isteğe bağlı özniteliklerden, isteğe bağlı accessor_modifier, belirteç get veya set, ve ardından bir accessor_body oluşur.

Bir ref-değerli özellik için ref_get_accessor_declaration, isteğe bağlı öznitelikler, isteğe bağlı bir accessor_modifier, belirteç get ve ardından bir ref_accessor_body oluşur.

accessor_modifierkullanımı aşağıdaki kısıtlamalara tabidir:

  • accessor_modifierreadonly yalnızca doğrudan bir struct_declaration (§16.4.11, §16.4.13) tarafından kapsanan bir property_declaration veya indexer_declaration içinde izin verilir.
  • Değiştiricisi olmayan override bir özellik veya dizin oluşturucu için, accessor_modifier yalnızca özellik veya dizin oluşturucunun hem alıcı hem de ayarlayıcısı varsa ve yalnızca bu erişimcilerden birinde izin verilirse kullanılabilir.
  • override değiştirici içeren bir özellik veya dizin oluşturucu için, bir erişimci varsa geçersiz kılınan erişimcinin accessor_modifier ile eşleşmelidir.
  • accessor_modifier, özelliğin veya dizin oluşturucunun bildirilen erişilebilirliğinin kendisinden kesinlikle daha kısıtlayıcı bir erişilebilirlik bildirecektir. Kesin olmak gerekirse:
    • Özelliğin veya dizin oluşturucunun bildirilen erişilebilirliği publicvarsa, accessor_modifier tarafından bildirilen erişilebilirlik , private protected, protected internal, internalveya protectedolabilirprivate.
    • Özelliğin veya dizin oluşturucunun bildirilen erişilebilirliği protected internalvarsa, accessor_modifier tarafından bildirilen erişilebilirlik , private protected, protected private, internalveya protectedolabilirprivate.
    • Özelliğin veya dizin oluşturucunun veya için bildirilen bir erişilebilirliği internal varsa, accessor_modifierprotectedbildirilen erişilebilirlik veya olmalıdırprivate protected.private
    • Özelliğin veya dizin oluşturucunun erişilebilirliği private protectedbildirildiyse, accessor_modifier tarafından bildirilen erişilebilirlik olacaktırprivate.
    • Özelliğin veya dizin oluşturucunun erişilebilirliği privatebildirildiyse, accessor_modifier kullanılamaz.

Başvuru abstract değeri olmayan ve extern olmayan özellikler için, belirtilen her erişimci için herhangi bir accessor_body yalnızca noktalı virgüldür. Soyut olmayan, extern olmayan ancak dizin oluşturucu olmayan bir özellikte, belirtilen tüm erişimcilerin accessor_body yalnızca bir noktalı virgül ile tanımlanmış olabilir; bu durumda, bu bir otomatik olarak uygulanan özellik olur (§15.7.4). Otomatik olarak uygulanan bir özelliğin en az bir get erişimcisi olmalıdır. Soyut olmayan ve extern olmayan diğer herhangi bir özelliğin erişimcileri için, accessor_body şu olabilir:

  • ilgili erişimci çağrıldığında yürütülecek deyimleri belirten bir blok ; veya
  • bir ifade gövdesi, bir => ardından bir ifade ve bir noktalı virgül içerir ve bu gövde, ilgili erişimci çağrıldığında yürütülecek tek bir ifadeyi belirtir.

abstract ve extern ref değerli özellikler için ref_accessor_body sadece bir noktalı virgüldür. Soyut olmayan, dışsal olmayan diğer herhangi bir özelliğin erişimcisi için ref_accessor_body ya:

  • get erişimcisi çağrıldığında yürütülecek deyimleri belirten bir blok ; veya
  • Bir ifade gövdesi, => ve ref ile başlayan, ardından variable_reference ve bir noktalı virgül içeren bir yapıdır. Get metodu çağrıldığında değişken referansı değerlendirilir.

Ref değerli olmayan bir özellik için get erişimcisi, özellik türü dönüş değerine sahip parametresiz bir yönteme karşılık gelir. Atamanın hedefi dışında, bir ifadede böyle bir özelliğe başvurulduğunda, özelliğin değerini hesaplamak için get erişimcisi çağrılır (§12.2.2).

Ref değerli olmayan bir özellik için get erişimcisinin gövdesi, §15.6.11'de açıklanan değer döndüren yöntemlere dair kurallara uygun olmalıdır. Özellikle, get erişimcisinin gövdesindeki tüm return ifadeler, örtük olarak özellik türüne dönüştürülebilir bir ifade belirtecektir. Ayrıca, get erişimcisinin uç noktasına ulaşılamaz.

Ref değerli bir özelliğin get erişimcisi, özellik türündeki bir değişkene variable_reference dönüş değerine sahip parametresiz bir yönteme karşılık gelir. Böyle bir özelliğe bir ifadede başvurulduğunda, özelliğin variable_reference değerini hesaplamak için get erişimcisi çağrılır. Bu değişken referansı, diğerleri gibi, salt okunur olmayan değişken referansları için başvurulan değişkeni okumak veya bağlamın gerektirdiği şekilde yazmak için kullanılır.

Örnek: Aşağıdaki örnek, ref değerli bir özelliği atamanın hedefi olarak gösterir:

class Program
{
    static int field;
    static ref int Property => ref field;

    static void Main()
    {
        field = 10;
        Console.WriteLine(Property); // Prints 10
        Property = 20;               // This invokes the get accessor, then assigns
                                     // via the resulting variable reference
        Console.WriteLine(field);    // Prints 20
    }
}

son örnek

Ref değerli bir özellik için get erişimcisinin gövdesi, §15.6.11'de açıklanan başvuru değerli yöntemlerin kurallarına uygun olmalıdır.

Atama erişimcisi, özellik türünde tek bir değer parametresine ve void dönüş türüne sahip bir yönteme karşılık gelir. Küme erişimcisinin örtük parametresinin adı her zaman value olarak belirlenir. Bir özelliğe bir atamanın hedefi (§12.23) veya veya işleneni ++ olarak başvurulduğunda veya –- (§12.8.16, §12.9.7), küme erişimcisi yeni değeri sağlayan bir bağımsız değişkenle çağrılır (§12.23.2). Bir set aksesuarının gövdesi , §15.6.11'de voidaçıklanan yöntemler için kurallara uyacaktır. Özellikle, set erişimci gövdesindeki dönüş ifadelerinin bir ifade belirtmesine izin verilmez. Bir set erişimcisi, value adlı bir parametreye örtük olarak sahip olduğundan, bir set erişimcisindeki yerel değişken veya sabit bildirimin bu ada sahip olması derleme zamanı hatasına yol açar.

Alma ve ayarlama erişimcilerinin varlığına veya yokluğuna bağlı olarak, bir özellik aşağıdaki gibi sınıflandırılır:

  • Hem get erişimcisini hem de küme erişimcisini içeren bir özelliğin okuma-yazma özelliği olduğu söylenir.
  • Yalnızca get erişimcisine sahip bir özelliğin salt okunur bir özellik olduğu söylenir. Salt okunur özelliğin bir atamanın hedefi olması derleme zamanı hatasıdır.
  • Yalnızca ayarlanmış erişimciye sahip bir özelliğin salt yazma özelliği olduğu söylenir. Atamanın hedefi dışında, bir ifadedeki salt yazma özelliğine başvurmak derleme zamanı hatasıdır.

Not: Bu işleçler yenisini yazmadan önce işlenenlerinin eski değerini okuduğundan, ön ve sonek ++-- ile işleçler ve bileşik atama işleçleri salt yazma özelliklerine uygulanamaz. son not

Örnek: Aşağıdaki kodda

public class Button : Control
{
    private string caption;

    public string Caption
    {
        get => caption;
        set
        {
            if (caption != value)
            {
                caption = value;
                Repaint();
            }
        }
    }

    public override void Paint(Graphics g, Rectangle r)
    {
        // Painting code goes here
    }
}

Button denetimi bir ortak Caption özellik bildirir. Caption özelliğinin get erişimcisi, özel string alanda depolanan değerini döndürürcaption. Küme erişimcisi, yeni değerin geçerli değerden farklı olup olmadığını denetler ve farklıysa yeni değeri depolar ve denetimi yeniden boyar. Özellikler genellikle yukarıda gösterilen deseni izler: Get erişimcisi yalnızca bir alanda depolanan bir private değeri döndürür ve küme erişimcisi bu private alanı değiştirir ve nesnenin durumunu tam olarak güncelleştirmek için gereken ek eylemleri gerçekleştirir. Button Yukarıdaki sınıf göz önünde bulundurulduğunda, özelliğin kullanımına Caption bir örnek aşağıda verilmiştir:

Button okButton = new Button();
okButton.Caption = "OK"; // Invokes set accessor
string s = okButton.Caption; // Invokes get accessor

Burada, küme erişimcisi özelliğine bir değer atanarak çağrılır ve bir ifadedeki özelliğe başvurularak get erişimcisi çağrılır.

son örnek

Bir özelliğin get ve set erişimcileri ayrı üyeler değildir ve bir özelliğin erişimcilerini ayrı olarak bildirmek mümkün değildir.

Örnek: Örnek

class A
{
    private string name;

    // Error, duplicate member name
    public string Name
    { 
        get => name;
    }

    // Error, duplicate member name
    public string Name
    { 
        set => name = value;
    }
}

tek bir okuma-yazma özelliği bildirmez. Bunun yerine, biri salt okunur, diğeri salt yazma olan aynı ada sahip iki özellik bildirir. Aynı sınıfta bildirilen iki üyenin aynı ada sahip olamayacağından, örnek derleme zamanı hatasına yol açar.

son örnek

Türetilmiş bir sınıf, devralınan bir özellik ile aynı ada sahip bir özellik bildirdiğinde, türetilmiş özellik hem okuma hem de yazma bakımından devralınan özelliği gizler.

Örnek: Aşağıdaki kodda

class A
{
    public int P
    {
        set {...}
    }
}

class B : A
{
    public new int P
    {
        get {...}
    }
}

P özelliği B içinde, hem okuma hem de yazma bakımından, P özelliğini A gizler. Bu nedenle, açıklamalarda

B b = new B();
b.P = 1;       // Error, B.P is read-only
((A)b).P = 1;  // Ok, reference to A.P

içindeki salt okunur b.P özelliği içindeki salt yazma P özelliğini B gizlediğinden, ataması P bir derleme zamanı hatasının bildirilmesine Aneden olur. Bununla birlikte, bir atamanın gizli P özelliğe erişmek için kullanılabileceğini unutmayın.

son örnek

Genel alanlardan farklı olarak, özellikler bir nesnenin iç durumu ile ortak arabirimi arasında bir ayrım sağlar.

Örnek: Bir konumu temsil eden bir Point yapıyı kullanan aşağıdaki kodu göz önünde bulundurun:

class Label
{
    private int x, y;
    private string caption;

    public Label(int x, int y, string caption)
    {
        this.x = x;
        this.y = y;
        this.caption = caption;
    }

    public int X => x;
    public int Y => y;
    public Point Location => new Point(x, y);
    public string Caption => caption;
}

Burada, Label sınıfı konumunu depolamak için int ve x alanlarını kullanır. Konum, hem bir X ve bir Y özelliği olarak hem de bir Location türünde Point özelliği olarak kamuya açıklanmıştır. öğesinin Labelgelecekteki bir sürümünde konumu dahili olarak Point depolamak daha kullanışlı hale gelirse, değişiklik sınıfın genel arabirimini etkilemeden yapılabilir:

class Label
{
    private Point location;
    private string caption;

    public Label(int x, int y, string caption)
    {
        this.location = new Point(x, y);
        this.caption = caption;
    }

    public int X => location.X;
    public int Y => location.Y;
    public Point Location => location;
    public string Caption => caption;
}

Eğer x ve y alan olsaydı, public readonly sınıfında böyle bir değişiklik yapmak imkansız olurdu.

son örnek

Not: Durumu özellikler aracılığıyla açığa çıkarmak, alanları doğrudan açığa çıkarmak kadar verimli olabilir. Özellikle, bir özellik sanal olmayan ve yalnızca az miktarda kod içerdiğinde, yürütme ortamı erişimcilere yapılan çağrıları erişimcilerin gerçek koduyla değiştirebilir. Bu işlem, inlining olarak bilinir ve özellik erişimini alan erişimi kadar verimli hale getirir, ancak özelliklerin artan esnekliğini korur. son not

Örnek: Get erişimcisini çağırmak kavramsal olarak bir alanın değerini okumakla eşdeğer olduğundan, erişimcilerin gözlemlenebilir yan etkilere sahip olması için kötü programlama stili olarak kabul edilir. Örnekte

class Counter
{
    private int next;

    public int Next => next++;
}

özelliğinin Next değeri, özelliğin daha önce kaç kez erişildiğine bağlıdır. Bu nedenle, özelliğine erişmek gözlemlenebilir bir yan etki oluşturur ve özelliğin bunun yerine bir yöntem olarak uygulanması gerekir.

Get erişimcileri için "yan efekt yok" kuralı, get erişimcilerinin her zaman yalnızca alanlarda depolanan değerleri döndürmek için yazılması gerektiği anlamına gelmez. Aslında, get erişimcileri genellikle birden çok alana erişerek veya yöntemleri çağırarak bir özelliğin değerini hesaplar. Ancak, düzgün tasarlanmış bir get erişimcisi nesnenin durumunda gözlemlenebilir değişikliklere neden olan hiçbir eylem gerçekleştirmez.

son örnek

Özellikler, kaynağın başlatılmasını ilk başvurulana kadar geciktirmek için kullanılabilir.

Örnek:

public class Console
{
    private static TextReader reader;
    private static TextWriter writer;
    private static TextWriter error;

    public static TextReader In
    {
        get
        {
            if (reader == null)
            {
                reader = new StreamReader(Console.OpenStandardInput());
            }
            return reader;
        }
    }

    public static TextWriter Out
    {
        get
        {
            if (writer == null)
            {
                writer = new StreamWriter(Console.OpenStandardOutput());
            }
            return writer;
        }
    }

    public static TextWriter Error
    {
        get
        {
            if (error == null)
            {
                error = new StreamWriter(Console.OpenStandardError());
            }
            return error;
        }
    }
...
}

Console sınıfı, sırasıyla standart girişi, çıkışı ve hata cihazlarını temsil eden üç özellik içerir: In, Out ve Error. Bu üyeler özellik olarak gösterilerek, Console sınıf gerçekten kullanılana kadar başlatmalarını geciktirebilir. Örneğin, Out özelliğine ilk kez başvurulduğunda

Console.Out.WriteLine("hello, world");

çıkış cihazının temeli TextWriter oluşturulur. Ancak, uygulama In ve Error özelliklerine referans yapmazsa, bu cihazlar için hiçbir nesne oluşturulmaz.

son örnek

15.7.4 Otomatik olarak uygulanan özellikler

Otomatik olarak uygulanan bir özellik (veya kısaca otomatik özellik), yalnızca noktalı virgül içeren erişimcinin gövdesi olan soyut olmayan, extern olmayan, başvuru değeri olmayan bir özelliktir. Otomatik özellikler bir get erişimciye sahip olmalı ve isteğe bağlı olarak bir set erişimciye sahip olabilir.

Bir özellik otomatik olarak uygulanan bir özellik olarak belirtildiğinde, özellik için gizli bir yedekleme alanı otomatik olarak kullanılabilir ve erişimciler bu yedekleme alanından okumak ve bu alana yazmak için uygulanır. Gizli yedekleme alanına erişilemez, yalnızca otomatik olarak uygulanan özellik erişimcileri aracılığıyla, hatta içeren tür içinde bile okunabilir ve yazılabilir. Otomatik özelliğin ayarlama erişimcisi yoksa, yedekleme alanı readonly olarak kabul edilir (§15.5.3). Bir readonly alan gibi, kapsayan sınıfın oluşturucusunun gövdesinde otomatik salt okunur özellik de atanabilir. Böyle bir atama doğrudan özelliğin salt okunur yedekleme alanına atanır.

Bir otomatik değişkene isteğe bağlı olarak, doğrudan yedekleme alanına değişken başlatıcısı (§17.7) olarak uygulanan bir özellik başlatıcısı (property_initializer) olabilir.

Örnek:

public class Point
{
    public int X { get; set; } // Automatically implemented
    public int Y { get; set; } // Automatically implemented
}

aşağıdaki bildirime eşdeğerdir:

public class Point
{
    private int x;
    private int y;

    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}

son örnek

Örnek: Aşağıdakilerde

public class ReadOnlyPoint
{
    public int X { get; }
    public int Y { get; }

    public ReadOnlyPoint(int x, int y)
    {
        X = x;
        Y = y;
    }
}

aşağıdaki bildirime eşdeğerdir:

public class ReadOnlyPoint
{
    private readonly int __x;
    private readonly int __y;
    public int X { get { return __x; } }
    public int Y { get { return __y; } }

    public ReadOnlyPoint(int x, int y)
    {
        __x = x;
        __y = y;
    }
}

Salt okunur alana yapılan atamalar, oluşturucu içinde gerçekleştiğinden geçerlidir.

son örnek

Yedekleme alanı gizli olsa da, otomatik olarak uygulanan özelliğin property_declaration (§15.7.1) aracılığıyla bu alana doğrudan alan hedefli öznitelikler uygulanmış olabilir.

Örnek: Aşağıdaki kod

[Serializable]
public class Foo
{
    [field: NonSerialized]
    public string MySecret { get; set; }
}

, kod aşağıdaki gibi yazılmış gibi, alan hedefli özniteliğin NonSerialized derleyici tarafından oluşturulan yedekleme alanına uygulanmasıyla sonuçlanır:

[Serializable]
public class Foo
{
    [NonSerialized]
    private string _mySecretBackingField;
    public string MySecret
    {
        get { return _mySecretBackingField; }
        set { _mySecretBackingField = value; }
    }
}

son örnek

15.7.5 Erişilebilirlik

Erişimcinin accessor_modifier varsa erişimcinin erişilebilirlik etki alanı (§7.5.3), accessor_modifier bildirilen erişilebilirliği kullanılarak belirlenir. Erişimcinin accessor_modifier yoksa erişimcinin erişilebilirlik etki alanı, özelliğin veya dizin oluşturucunun bildirilen erişilebilirliği tarafından belirlenir.

accessor_modifier varlığı, üye aramasını (§12.5) veya aşırı yükleme çözümlemesini (§12.6.4) hiçbir zaman etkilemez. Özellik veya dizin oluşturucudaki değiştiriciler, erişimin bağlamından bağımsız olarak her zaman hangi özelliğe veya dizin oluşturucuya bağlı olduğunu belirler.

Belirli bir başv değerli olmayan özellik veya başv değerli olmayan dizin oluşturucu seçildikten sonra, söz konusu erişimcilerin erişilebilirlik etki alanları bu kullanımın geçerli olup olmadığını belirlemek için kullanılır:

  • Kullanım bir değer olarak ise (§12.2.2), get erişimcisi mevcut ve erişilebilir olmalıdır.
  • Kullanım basit bir atamanın (§12.23.2) hedefiyse, set erişimcisi mevcut ve erişilebilir olmalıdır.
  • Kullanım bileşik atamanın hedefi (§12.23.4) veya ya da ++ işleçlerinin -- (§12.8.16, §12.9.7) hedefi olarak ise, hem alma erişimcileri hem de set erişimcisi mevcut ve erişilebilir olacaktır.

Örnek: Aşağıdaki örnekte, A.Text özelliği, sadece küme erişimcisinin çağrıldığı bağlamlarda bile, B.Text özelliği tarafından gizlenir. Buna karşılık, özelliği B.Count sınıfı Mtarafından erişilemez, bu nedenle bunun yerine erişilebilir özellik A.Count kullanılır.

class A
{
    public string Text
    {
        get => "hello";
        set { }
    }

    public int Count
    {
        get => 5;
        set { }
    }
}

class B : A
{
    private string text = "goodbye";
    private int count = 0;

    public new string Text
    {
        get => text;
        protected set => text = value;
    }

    protected new int Count
    {
        get => count;
        set => count = value;
    }
}

class M
{
    static void Main()
    {
        B b = new B();
        b.Count = 12;       // Calls A.Count set accessor
        int i = b.Count;    // Calls A.Count get accessor
        b.Text = "howdy";   // Error, B.Text set accessor not accessible
        string s = b.Text;  // Calls B.Text get accessor
    }
}

son örnek

Belirli bir başvuru değerli özellik veya başvuru değerli dizinleyici seçildikten sonra—kullanım bir değer olarak mı, basit bir atamanın hedefi mi yoksa bileşik atama hedefi mi olsun—ilgili get erişimcisinin erişilebilirlik alanı, bu kullanımın geçerli olup olmadığını belirlemek için kullanılır.

Arayüz uygulamak için kullanılan bir erişimcinin erişimci değiştiricisi olmamalıdır. Arabirim uygulamak için yalnızca bir erişimci kullanılıyorsa, diğer erişimci bir accessor_modifier ile bildirilebilir:

Örnek:

public interface I
{
    string Prop { get; }
}

public class C : I
{
    public string Prop
    {
        get => "April";     // Must not have a modifier here
        internal set {...}  // Ok, because I.Prop has no set accessor
    }
}

son örnek

15.7.6 Sanal, mühürlü, geçersiz kılma ve soyut erişimciler

Not: Bu alt kural hem özellikler (§15.7) hem de dizin oluşturucular (§15.9) için geçerlidir. Alt hüküm, dizin oluşturucular için okuma yaparken, özellikler açısından yazılır ve özellik/özellikler yerine dizin oluşturucu/dizin oluşturucuları kullanılır. §15.9.2'de verilen özellikler ile dizin oluşturucular arasındaki farklar listesine bakın. son not

Sanal özellik bildirimi, özelliğin erişimcilerinin sanal olduğunu belirtir. virtual değiştiricisi, bir özelliğin özel olmayan tüm erişimcilerine uygulanır. Sanal özelliğin erişimcisinde privateaccessor_modifier olduğunda, özel erişimci örtük olarak sanal değildir.

Soyut özellik bildirimi, özelliğin erişimcilerinin sanal olduğunu belirtir, ancak erişimcilerin gerçek bir uygulamasını sağlamaz. Bunun yerine, soyut olmayan türetilmiş sınıfların özelliği geçersiz kılarak erişimciler için kendi uygulamalarını sağlaması gerekir. Soyut özellik bildirimine yönelik bir erişimci gerçek bir uygulama sağlamadığından , accessor_body yalnızca noktalı virgülden oluşur. Soyut bir özelliğin aksesuarı private olmamalıdır.

abstract ve override değiştiricilerini içeren bir özellik bildirimi, özelliğin soyut olduğunu belirtir ve bir temel özelliği geçersiz kıldığını belirtir. Bu tür bir özelliğin erişimcileri de soyuttur.

Soyut özellik bildirimlerine yalnızca soyut sınıflarda (§15.2.2.2) ve arabirimlerde (§19.4.4) izin verilir. Devralınan bir sanal özelliğin erişimcileri, bir yönergeyi belirten bir özellik bildirimi dahil edilerek türetilmiş bir override sınıfta geçersiz kılınabilir. Bu, geçersiz kılma özelliği bildirimi olarak bilinir. Aşan özellik bildirimi yeni bir özellik bildirmez. Bunun yerine, yalnızca mevcut bir sanal özelliğin erişimcilerinin uygulamalarını özelleştirir.

Geçersiz kılma bildirimi ile geçersiz kılınan temel özelliğin aynı ilan edilen erişim seviyesine sahip olması gerekir. Başka bir deyişle, geçersiz kılma bildirimi temel özelliğin erişilebilirliğini değiştirmez. Ancak geçersiz kılınan temel özellik dahili olarak korunuyorsa ve geçersiz kılma bildirimini içeren derlemeden farklı bir derlemede bildiriliyorsa geçersiz kılma bildiriminin bildirilen erişilebilirliği korunmalıdır. Devralınan özelliğin yalnızca tek bir erişimcisi varsa (devralınan özellik salt okunur veya salt okunursa), geçersiz kılma özelliği yalnızca bu erişimciyi içermelidir. Devralınan özellik her iki erişimciyi de içeriyorsa (devralınan özellik okuma-yazma ise), geçersiz kılma özelliği tek bir erişimci veya her iki erişimciyi de içerebilir. Geçersiz kılma türü ile devralınan özellik arasında bir kimlik dönüştürmesi olacaktır.

Geçersiz kılınan özellik bildirimi değiştiriciyi sealed içerebilir. Bu değiştiricinin kullanılması, türetilmiş bir sınıfın özelliğini daha fazla geçersiz kılmasını engeller. Kilitli bir özelliğin erişicileri de kilitlidir.

Bildirim ve çağırma söz dizimindeki farklar dışında, sanal, korumalı, geçersiz kılma ve soyut erişimciler, bu yöntemlerin tam olarak aynı şekilde, yani sanal, korumalı, geçersiz kılma ve soyut yöntemler gibi davranır. Özellikle, §15.6.4, §15.6.5, §15.6.6 ve §15.6.7'de açıklanan kurallar, erişimciler ilgili formun yöntemleriymiş gibi uygulanır:

  • Get erişimcisi, özellik türünün dönüş değerine ve içeren özellikle aynı değiştiricilere sahip parametresiz bir yönteme karşılık gelir.
  • Küme erişimcisi özellik türünün tek bir değer parametresine, geçersiz dönüş türüne ve içeren özellikle aynı değiştiricilere sahip bir yönteme karşılık gelir.

Örnek: Aşağıdaki kodda

abstract class A
{
    int y;

    public virtual int X
    {
        get => 0;
    }

    public virtual int Y
    {
        get => y;
        set => y = value;
    }

    public abstract int Z { get; set; }
}

X sanal salt okunur bir özelliktir, Y sanal bir okuma-yazma özelliğidir ve Z soyut bir okuma-yazma özelliğidir. Soyut Z olduğundan, A içeren sınıf da soyut olarak bildirilmelidir.

öğesinden A türetilen bir sınıf aşağıda gösterilmiştir:

class B : A
{
    int z;

    public override int X
    {
        get => base.X + 1;
    }

    public override int Y
    {
        set => base.Y = value < 0 ? 0: value;
    }

    public override int Z
    {
        get => z;
        set => z = value;
    }
}

Burada , Xve Y bildirimleri Zözellik bildirimlerini geçersiz kılmaktadır. Her özellik bildirimi, ilgili devralınan özelliğin erişilebilirlik değiştiricileri, türü ve adıyla tam olarak eşleşir. Devralınan X erişimcilere erişmek için get erişimcisi ve küme erişimcisi Y temel anahtar sözcüğünü kullanın. Z bildirimi her iki soyut erişimciyi de geçersiz kılar, bu nedenle abstract içinde bekleyen B işlev üyesi yoktur ve B sınıfının soyut olmaması mümkündür.

son örnek

Bir özellik geçersiz kılma olarak bildirildiğinde geçersiz kılınan erişimcilere geçersiz kılınan kod erişilebilir olacaktır. Buna ek olarak, hem özelliğin hem de dizin oluşturucunun kendisinin ve erişimcilerin bildirilen erişilebilirliği, geçersiz kılınan üye ve erişimcilerinkiyle eşleşecektir.

Örnek:

public class B
{
    public virtual int P
    {
        get {...}
        protected set {...}
    }
}

public class D: B
{
    public override int P
    {
        get {...}            // Must not have a modifier here
        protected set {...}  // Must specify protected here
    }
}

son örnek

15.8 Olaylar

15.8.1 Genel

Olay, bir nesnenin veya sınıfın bildirim sağlamasını sağlayan bir üyedir. İstemciler, olay işleyicilerisağlayarak olaylar için yürütülebilir kod ekleyebilir.

Olaylar event_declarationkullanılarak bildirilir:

event_declaration
    : attributes? event_modifier* 'event' type variable_declarators ';'
    | attributes? event_modifier* 'event' type member_name
        '{' event_accessor_declarations '}'
    ;

event_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'readonly'        // direct struct members only
    | unsafe_modifier   // unsafe code support
    ;

event_accessor_declarations
    : add_accessor_declaration remove_accessor_declaration
    | remove_accessor_declaration add_accessor_declaration
    ;

add_accessor_declaration
    : attributes? 'add' block
    ;

remove_accessor_declaration
    : attributes? 'remove' block
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

event_declaration bir öznitelik kümesi (§23) ve izin verilen bildirilen erişilebilirlik türlerinden herhangi birini (§15.3.6),new (§15.3.5), static (§15.6.3, §15.8.4), (§15.6.4, virtual§15.8.5), (override, §15.8.5), (sealed), (abstract, §15.8.5) ve (extern) değiştiriciler. Ayrıca, doğrudan bir struct_declaration içeren bir event_declaration değiştiriciyi içerebilir readonly (§16.4.12).

Olay bildirimleri, geçerli değiştirici birleşimleriyle ilgili olarak yöntem bildirimleriyle (§15.6) aynı kurallara tabidir.

Olay bildiriminin türü bir delegate_type (§8.2.8) ve bu delegate_type en az olayın kendisi kadar erişilebilir olacaktır (§7.5.5).

Olay bildirimi event_accessor_declarationiçerebilir. Ancak, dışsal olmayan, soyut olmayan olaylar için derleyici bunları otomatik olarak sağlayacaktır (§15.8.2); extern olaylar için ise erişim işlevleri dışarıdan sağlanır.

event_accessor_declaration'leri atlayan bir olay bildirimi, her variable_declaratoriçin bir veya daha fazla olaytanımlar. Öznitelikler ve değiştiriciler, böyle bir event_declaration tarafından bildirilen tüm üyeler için geçerlidir.

Bir event_declaration'ın hem abstract değiştiriciyi hem de event_accessor_declaration'ları içermesi derleme zamanı hatasıdır.

Bir olay bildirimi bir extern değiştirici içerdiğinde, olayın bir dış olay olduğu söylenir. Dış olay bildirimi gerçek bir uygulama sağlamadığından, hem değiştiriciyi externhem de event_accessor_declarationdahil etmesi bir hatadır.

Bir veya abstract değiştirici içeren bir olay bildiriminin external'ında variable_initializer bulundurulması derleme zamanı hatasıdır.

Bir olay, += ve -= işleçlerinin sol işleneni olarak kullanılabilir. Bu işleçler sırasıyla olay işleyicileri eklemek veya bir olaydan olay işleyicilerini kaldırmak için kullanılır ve olay erişim değiştiricileri bu tür işlemlere izin verilen bağlamları denetler.

Bir olayda, olayın bildirildiği türün dışında olan kod tarafından izin verilen tek işlemler ve +='dir-=. Bu nedenle, bu tür kod bir olay için işleyici ekleyip kaldırabilir, ancak olay işleyicilerinin temel listesini doğrudan alamaz veya değiştiremez.

veya biçimindeki x += y bir işlemde, ne zaman x –= y bir olay olduğunda işlemin sonucu türüne x (void) sahiptir (türüne sahip olmak yerine, atamadan sonra değerinix, olay olmayan türlerde tanımlanan diğer x ve += işleçleri için olduğu -=gibi). Bu, dış kodun bir olayın temel temsilcisini dolaylı olarak incelemesini engeller.

Örnek: Aşağıdaki örnek, olay işleyicilerinin sınıfın Button örneklerine nasıl eklendiğini gösterir:

public delegate void EventHandler(object sender, EventArgs e);

public class Button : Control
{
    public event EventHandler Click;
}

public class LoginDialog : Form
{
    Button okButton;
    Button cancelButton;

    public LoginDialog()
    {
        okButton = new Button(...);
        okButton.Click += new EventHandler(OkButtonClick);
        cancelButton = new Button(...);
        cancelButton.Click += new EventHandler(CancelButtonClick);
    }

    void OkButtonClick(object sender, EventArgs e)
    {
        // Handle okButton.Click event
    }

    void CancelButtonClick(object sender, EventArgs e)
    {
        // Handle cancelButton.Click event
    }
}

Burada örnek LoginDialog oluşturucu iki Button örnek oluşturur ve olaylara Click olay işleyicileri ekler.

son örnek

15.8.2 Alan benzeri olaylar

Bir olayın bildirimini içeren sınıfın veya yapının program metni içinde, bazı olaylar alanlar gibi kullanılabilir. Bu şekilde kullanılmak için bir olay, soyut veya extern olmamalı ve açıkça event_accessor_declaration içermemelidir. Böyle bir olay, alana izin veren herhangi bir bağlamda kullanılabilir. alanı, olaya eklenmiş olay işleyicileri listesine başvuran bir temsilci (§21) içerir. Hiçbir olay işleyicisi eklenmediğinde, alan null içerir.

Örnek: Aşağıdaki kodda

public delegate void EventHandler(object sender, EventArgs e);

public class Button : Control
{
    public event EventHandler Click;

    protected void OnClick(EventArgs e)
    {
        EventHandler handler = Click;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    public void Reset() => Click = null;
}

Click sınıfı içinde Button bir alan olarak kullanılır. Örnekte gösterildiği gibi, alan incelenebilir, değiştirilebilir ve temsilci çağırma ifadelerinde kullanılabilir. OnClick sınıfındaki Button yöntemi Click etkinliğini "tetikler". Olay oluşturma özelliği, olay tarafından temsil edilen temsilcinin çağrılmasıyla tam olarak eşdeğerdir; bu nedenle, olayları oluşturmak için özel dil yapıları yoktur. Temsilci çağrısının önünde, temsilcinin null olmamasını ve denetimin yerel bir kopya üzerinde yapılarak iş parçacığı güvenliğinin sağlanmasını temin eden bir kontrol yapıldığını unutmayın.

Button sınıfının bildirimi dışında, Click üyesi yalnızca += ve –= işleçlerinin sol tarafında kullanılabilir, olarak gibi.

b.Click += new EventHandler(...);

olayın çağrı listesine Click bir temsilci ekler ve

Click –= new EventHandler(...);

bir temsilciyi olayın çağrı listesinden Click kaldırır.

son örnek

Alan benzeri bir olayı derlerken, derleyici temsilciyi tutmak için otomatik olarak depolama alanı oluşturur ve temsilci alanına olay işleyicileri ekleyen veya kaldıran olay için erişimciler oluşturur. Ekleme ve kaldırma işlemleri iş parçacığı güvenlidir ve bir örnek olayını içeren nesne üzerinde kilit (§13.13) veya System.Type statik bir olay için nesne (§12.8.18) tutularak yapılabilir (ancak yapılması gerekmez).

Not: Bu nedenle, formun örnek olay bildirimi:

class X
{
    public event D Ev;
}

aşağıdakilere eşdeğer bir değerle derlenmelidir:

class X
{
    private D __Ev; // field to hold the delegate

    public event D Ev
    {
        add
        {
            /* Add the delegate in a thread safe way */
        }
        remove
        {
            /* Remove the delegate in a thread safe way */
        }
    }
}

sınıf X içinde, Ev ve += işleçlerinin sol tarafındaki –= referanslar, ekleme ve kaldırma erişimcilerini tetikler. Tüm diğer Ev başvuruları gizli alan __Ev'i referans almak için derlenir (§12.8.7). "__Ev" adı rastgeledir; gizli alanın herhangi bir adı olabilir veya hiç adı yoktur.

son not

15.8.3 Olay erişimcileri

Not: Etkinlik bildirimleri genellikle event_accessor_declaration içermez, yukarıdaki Button örnekte olduğu gibi. Örneğin, olay başına bir alanın depolama maliyeti kabul edilebilir değilse bunlar dahil edilebilir. Böyle durumlarda, bir sınıf event_accessor_declarationiçerebilir ve olay işleyicileri listesini depolamak için özel bir mekanizma kullanabilir. son not

Bir etkinliğin event_accessor_declarations, olay işleyicileri ekleme ve kaldırma ile ilişkili yürütülebilir deyimleri belirtir.

Erişimci bildirimleri bir add_accessor_declaration ve bir remove_accessor_declaration oluşur. Her bir erişimci bildirimi, ekleme veya kaldırma belirteci ve ardından bir blok içerir. bloğu bir add_accessor_declaration ile ilişkili olup bir olay işleyicisi eklendiğinde yürütülecek deyimleri belirtir ve blok bir remove_accessor_declaration ile ilişkili olup bir olay işleyicisi kaldırıldığında yürütülecek deyimleri belirtir.

Her add_accessor_declaration ve remove_accessor_declaration , olay türünün tek bir değer parametresine ve dönüş türüne sahip bir yönteme void karşılık gelir. Olay erişimcisinin örtük parametresi value olarak adlandırılır. Bir olay ataması işlemi sırasında bir olay kullanıldığında, uygun olay erişimcisi kullanılır. Özellikle, atama işleci += ise ekleme erişimcisi kullanılır ve atama işleci –= ise kaldırma erişimcisi kullanılır. Her iki durumda da, atama işlecinin sağ işleneni olay erişimcisinin bağımsız değişkeni olarak kullanılır. Add_accessor_declaration veya remove_accessor_declaration bloğu, §15.6.9'da açıklanan yöntemlerin kurallarına uygun olmalıdır. Özellikle, return böyle bir bloktaki deyimlerin bir ifade belirtmesine izin verilmez.

Olay erişimcisi, örtük olarak value adlı bir parametreye sahip olduğundan, bir olay erişimcisinde bildirilen yerel bir değişken veya sabitin bu ada sahip olması derleme zamanı hatasıdır.

Örnek: Aşağıdaki kodda


class Control : Component
{
    // Unique keys for events
    static readonly object mouseDownEventKey = new object();
    static readonly object mouseUpEventKey = new object();

    // Return event handler associated with key
    protected Delegate GetEventHandler(object key) {...}

    // Add event handler associated with key
    protected void AddEventHandler(object key, Delegate handler) {...}

    // Remove event handler associated with key
    protected void RemoveEventHandler(object key, Delegate handler) {...}

    // MouseDown event
    public event MouseEventHandler MouseDown
    {
        add { AddEventHandler(mouseDownEventKey, value); }
        remove { RemoveEventHandler(mouseDownEventKey, value); }
    }

    // MouseUp event
    public event MouseEventHandler MouseUp
    {
        add { AddEventHandler(mouseUpEventKey, value); }
        remove { RemoveEventHandler(mouseUpEventKey, value); }
    }

    // Invoke the MouseUp event
    protected void OnMouseUp(MouseEventArgs args)
    {
        MouseEventHandler handler;
        handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey);
        if (handler != null)
        {
            handler(this, args);
        }
    }
}

Control sınıfı olaylar için bir iç depolama mekanizması uygular. AddEventHandler yöntemi bir temsilci değerini bir anahtarla ilişkilendirir, GetEventHandler yöntemi şu anda bir anahtarla ilişkilendirilmiş olan temsilciyi döndürür ve RemoveEventHandler yöntem, belirtilen olay için bir olay işleyicisi olarak bir temsilciyi kaldırır. Muhtemelen, temel depolama mekanizması, bir anahtarla null delege değeri ilişkilendirrmenin bir maliyeti olmayacak şekilde tasarlanmıştır ve bu nedenle işlenmemiş olaylar depolama alanı tüketmez.

son örnek

15.8.4 Statik ve örnek olayları

Bir olay bildirimi bir static değiştirici içerdiğinde, olayın statik bir olay olduğu söylenir. Değiştirici olmadığında static , olayın bir örnek olayı olduğu söylenir.

Statik olay belirli bir örnekle ilişkilendirilmemiştir ve statik bir olayın erişimcilerinde this'a başvurmak derleme zamanı hatasıdır.

Bir örnek olayı, sınıfın belirli bir örneğiyle ilişkilendirilir ve bu örneğe bu olayın erişimcilerinde (this) (§12.8.14) olarak erişilebilir.

Statik ve örnek üyeleri arasındaki farklar §15.3.8'de daha ayrıntılı olarak ele alınıyor.

15.8.5 Sanal, korumalı, geçersiz kılma ve soyut erişimciler

Sanal olay bildirimi, bu olayın erişimcilerinin sanal olduğunu belirtir. virtual değiştirici, bir olayın her iki erişimcisine uygulanır.

Soyut olay bildirimi, olayın erişimcilerinin sanal olduğunu belirtir, ancak erişimcilerin gerçek bir uygulamasını sağlamaz. Bunun yerine, soyut olmayan türetilmiş sınıfların olayı geçersiz kılarak erişimciler için kendi uygulamalarını sağlaması gerekir. Soyut olay bildirimine yönelik bir erişimci gerçek bir uygulama sağlamadığından, event_accessor_declarationsağlamaz.

Hem abstract hem de override değiştiricilerini içeren bir olay bildirimi, olayın soyut olduğunu belirtir ve bir temel olayı geçersiz kılar. Böyle bir olayın erişimcileri de soyut.

Soyut olay bildirimlerine yalnızca soyut sınıflarda (§15.2.2.2) ve arabirimlerde (§19.4.5) izin verilir.

Devralınan bir sanal olayın erişimcileri, bir değiştiriciyi belirten bir olay bildirimi eklenerek türetilmiş bir override sınıfta geçersiz kılınabilir. Bu, geçersiz kılma olay bildirimi olarak bilinir. Geçersiz kılınan bir olay bildirimi, yeni bir olay oluşturmaz. Bunun yerine, mevcut bir sanal olayın erişimcilerinin uygulamalarını özelleştirmek yeterlidir.

Geçersiz kılınan olay bildirimi, geçersiz kılınan olayla tam olarak aynı erişilebilirlik değiştiricilerini ve adını belirtmelidir, geçersiz kılınan olayın türü ile geçersiz kılınan olay arasında bir kimlik dönüştürmesi olacaktır ve hem ekleme hem de kaldırma erişimcileri bildirimde belirtilmelidir.

Bir olay bildirimini geçersiz kılma modifikatörü sealed içerebilir. Değiştirici kullanımı this türetilmiş bir sınıfın olayı daha fazla geçersiz kılmasını engeller. Korumalı bir olayın erişimcileri de korumalıdır.

Geçersiz kılma olay bildiriminin new değiştiriciyi içermesi, derleme zamanı hatasıdır.

Bildirim ve çağırma söz dizimindeki farklar dışında, sanal, korumalı, geçersiz kılma ve soyut erişimciler, bu yöntemlerin tam olarak aynı şekilde, yani sanal, korumalı, geçersiz kılma ve soyut yöntemler gibi davranır. Özellikle, §15.6.4, §15.6.5, §15.6.6 ve §15.6.7'de açıklanan kurallar, erişimciler ilgili formun yöntemleriymiş gibi uygulanır. Her erişimci, olay türünün tek bir değer parametresine, dönüş türüne ve içeren olayla aynı değiştiricilere sahip bir void yönteme karşılık gelir.

15.9 Dizin Oluşturucular

15.9.1 Genel

Dizinleyici indexer, bir nesnenin bir dizi gibi dizine alınabilmesini sağlayan bir üyedir. Dizin oluşturucular indexer_declarationkullanılarak bildirilir:

indexer_declaration
    : attributes? indexer_modifier* indexer_declarator indexer_body
    | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body
    ;

indexer_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'readonly'        // direct struct members only
    | unsafe_modifier   // unsafe code support
    ;

indexer_declarator
    : type 'this' '[' parameter_list ']'
    | type interface_type '.' 'this' '[' parameter_list ']'
    ;

indexer_body
    : '{' accessor_declarations '}' 
    | '=>' expression ';'
    ;  

ref_indexer_body
    : '{' ref_get_accessor_declaration '}'
    | '=>' 'ref' variable_reference ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

indexer_declaration bir öznitelik kümesi (§23) ve izin verilen bildirilen erişilebilirlik türlerinden herhangi birini (§15.3.6), new (§15.3.5), virtual (§15.15.) içerebilir. 6.4), override (§15.6.5), sealed (§15.6.6), abstract (§15.6.7) ve extern (§15.6.8) değiştiricileri. Ayrıca, doğrudan bir struct_declaration tarafından içeri alınan bir indexer_declarationreadonly değiştiricisini içerebilir (§16.4.12).

  • İlki, başv değeri olmayan bir dizin oluşturucu bildirir. Türü type olan değer. Bu tür bir dizin oluşturucu okunabilir ve/veya yazılabilir olabilir.
  • İkincisi, başvuru değerli bir indeksleyiciyi bildirir. Değeri, türünde bir değişken için olan bir variable_reference (readonly) değeridir. Bu tür dizin oluşturucular yalnızca okunabilir.

bir indexer_declaration bir öznitelik kümesi (§23) ve izin verilen bildirilen erişilebilirlik türlerinden herhangi birini (§15.3.6), new (§15.3.5), virtual (§15.15.) içerebilir. 6.4), override (§15.6.5), sealed (§15.6.6), abstract (§15.6.7) ve extern (§15.6.8) değiştiricileri.

Dizin oluşturucu bildirimleri, geçerli değiştirici birleşimleriyle ilgili olarak yöntem bildirimleriyle (§15.6) aynı kurallara tabidir ve tek istisna, değiştiricinin static bir dizin oluşturucu bildiriminde izin verilmemesidir.

Dizin oluşturucu bildiriminin türü , bildirim tarafından tanıtılan dizin oluşturucunun öğe türünü belirtir.

Not: Dizin oluşturucular dizi öğesi benzeri bağlamlarda kullanılmak üzere tasarlandığından, bir dizi için tanımlanan öğe türü terimi de bir dizin oluşturucuyla birlikte kullanılır. son not

Dizin oluşturucu açık bir arabirim üyesi uygulaması değilse, türün ardından anahtar sözcüğü thisgelir. Açık arabirim üyesi uygulaması için türün ardından bir interface_type, bir "." ve anahtar sözcüğü thisgelir. Diğer üyelerin aksine, dizin oluşturucuların kullanıcı tanımlı adları yoktur.

parameter_list, dizin oluşturucunun parametrelerini belirtir. Dizin oluşturucunun parametre listesi bir yöntemin (§15.6.2) parametresine karşılık gelir; en az bir parametre belirtilmesi ve , thisve refout parametre değiştiricilerine izin verilmemesi dışında.

Bir dizin oluşturucunun türü ve parameter_list başvuruda bulunan türlerin her biri en az dizin oluşturucunun kendisi kadar erişilebilir olmalıdır (§7.5.5).

indexer_body bir deyim gövdesinden (§15.7.1) veya ifade gövdesinden (§15.6.1) oluşabilir. Bir deyim gövdesinde, ve { imleri ile çevrelenmiş olan }, dizin oluşturucusunun erişimcilerini (§15.7.3) bildirir. Erişimciler, dizin oluşturucu öğelerini okumak ve yazmakla ilişkili yürütülebilir deyimleri belirtir.

indexer_body ifadesinde "=>" ardından bir ifade E ve noktalı virgül içeren bir ifade gövdesi, { get { return E; } } ile tam olarak eşdeğerdir. Bu nedenle, yalnızca get erişimcisinin sonucunun tek bir ifade ile verildiği salt okunur indeksleyicileri belirtmek için kullanılabilir.

ref_indexer_body bir deyim gövdesinden veya ifade gövdesinden oluşabilir. Bir ifade gövdesinde bir get_accessor_declaration, dizinleyicinin get erişimcisini (§15.7.3) bildirir. Erişimci, dizinleyiciyi okuma işlemi ile ilgili yürütülebilir deyimleri belirtir.

Bir ref_indexer_body'de, => ve ref ile başlayan, bir variable_referenceV ve bir noktalı virgülden oluşan ifade gövdesi, { get { return ref V; } } deyim gövdesi ile tam olarak eşdeğerdir.

Not: Dizin oluşturucu öğesine erişim söz dizimi, dizi öğesiyle aynı olsa da, dizin oluşturucu öğesi değişken olarak sınıflandırılmamıştır. Bu nedenle, bir dizinleyici öğesini bir in, out veya ref bağımsız değişkeni olarak geçirmek, ancak dizinleyici ref-değere sahip ise ve dolayısıyla bir başvuru döndürüyorsa (§9.7) mümkündür. son not

Dizin oluşturucunun parameter_list , dizin oluşturucunun imzasını (§7.6) tanımlar. Özellikle, bir dizin oluşturucunun imzası parametrelerinin sayısından ve türlerinden oluşur. Parametrelerin öğe türü ve adları dizin oluşturucu imzasının bir parçası değildir.

Bir dizin oluşturucunun imzası, aynı sınıfta bildirilen diğer tüm dizin oluşturucuların imzalarından farklı olmalıdır.

Dizin oluşturucu bildirimi bir extern değiştirici içerdiğinde, dizin oluşturucunun bir dış dizin oluşturucu olduğu söylenir. Dış dizin oluşturucu bildirimi gerçek bir uygulama sağlamadığından, accessor_declarations içindeki her bir accessor_body noktalı virgül olmalıdır.

Örnek: Aşağıdaki örnekte, bit dizisindeki tek tek bitlere erişmek için dizin oluşturucu uygulayan bir sınıf bildirilir BitArray .

class BitArray
{
    int[] bits;
    int length;

    public BitArray(int length)
    {
        if (length < 0)
        {
            throw new ArgumentException();
        }
        bits = new int[((length - 1) >> 5) + 1];
        this.length = length;
    }

    public int Length => length;

    public bool this[int index]
    {
        get
        {
            if (index < 0 || index >= length)
            {
                throw new IndexOutOfRangeException();
            }
            return (bits[index >> 5] & 1 << index) != 0;
        }
        set
        {
            if (index < 0 || index >= length)
            {
                throw new IndexOutOfRangeException();
            }
            if (value)
            {
                bits[index >> 5] |= 1 << index;
            }
            else
            {
                bits[index >> 5] &= ~(1 << index);
            }
        }
    }
}

Sınıfının bir örneği BitArray , karşılık gelenden bool[] çok daha az bellek tüketir (öncekinin her değeri, ikincisininki byteyerine yalnızca bir bit kaplar), ancak ile bool[]aynı işlemlere izin verir.

Aşağıdaki CountPrimes sınıfı, 2 ile BitArray olarak verilen maksimum değer arasındaki asal sayıları hesaplamak için klasik "elek" algoritmasını kullanır.

class CountPrimes
{
    static int Count(int max)
    {
        BitArray flags = new BitArray(max + 1);
        int count = 0;
        for (int i = 2; i <= max; i++)
        {
            if (!flags[i])
            {
                for (int j = i * 2; j <= max; j += i)
                {
                    flags[j] = true;
                }
                count++;
            }
        }
        return count;
    }

    static void Main(string[] args)
    {
        int max = int.Parse(args[0]);
        int count = Count(max);
        Console.WriteLine($"Found {count} primes between 2 and {max}");
    }
}

öğelerine erişmek için söz diziminin BitArray tam olarak bir bool[]ile aynı olduğunu unutmayın.

Aşağıdaki örnek, 26×10 boyutunda ve iki parametreli bir dizin oluşturucuya sahip bir kılavuz sınıfını göstermektedir. İlk parametrenin A-Z aralığındaki büyük veya küçük harf olması, ikinci parametrenin ise 0-9 aralığındaki bir tamsayı olması gerekir.

class Grid
{
    const int NumRows = 26;
    const int NumCols = 10;
    int[,] cells = new int[NumRows, NumCols];

    public int this[char row, int col]
    {
        get
        {
            row = Char.ToUpper(row);
            if (row < 'A' || row > 'Z')
            {
                throw new ArgumentOutOfRangeException("row");
            }
            if (col < 0 || col >= NumCols)
            {
                throw new ArgumentOutOfRangeException ("col");
            }
            return cells[row - 'A', col];
        }
        set
        {
            row = Char.ToUpper(row);
            if (row < 'A' || row > 'Z')
            {
                throw new ArgumentOutOfRangeException ("row");
            }
            if (col < 0 || col >= NumCols)
            {
                throw new ArgumentOutOfRangeException ("col");
            }
            cells[row - 'A', col] = value;
        }
    }
}

son örnek

15.9.2 Dizin Oluşturucu ve Özellik Farklılıkları

Dizin oluşturucular ve özellikler kavram açısından çok benzerdir, ancak aşağıdaki yollarla farklılık gösterir:

  • Bir özellik adıyla tanımlanırken, dizin oluşturucu kendi imzası ile tanımlanır.
  • Bir özelliğe bir simple_name (§12.8.4) veya member_access (§12.8.7) üzerinden erişilirken, dizin oluşturucu öğesine bir element_access (§12.8.12.4) üzerinden erişilir.
  • Bir özellik statik üye olabilirken, dizin oluşturucu her zaman örnek üyesidir.
  • Bir özelliğin get erişimcisi parametresiz bir yönteme karşılık gelirken, dizin oluşturucunun get erişimcisi dizin oluşturucuyla aynı parametre listesine sahip bir yönteme karşılık gelir.
  • Bir özelliğin küme erişimcisi adlı valuetek bir parametreye sahip bir yönteme karşılık gelirken, dizin oluşturucunun küme erişimcisi dizin oluşturucuyla aynı parametre listesine ve adlı valueek parametreye sahip bir yönteme karşılık gelir.
  • Dizin oluşturucu erişimcisinin, dizin oluşturucu parametresiyle aynı ada sahip bir yerel değişken veya yerel sabit bildirmesi derleme zamanı hatasıyla sonuçlanır.
  • Geçersiz kılma özelliği bildiriminde, devralınan özelliğe söz dizimi base.Pkullanılarak erişilir ve burada P özellik adıdır. Geçersiz kılınan dizin oluşturucu bildiriminde, devralınan dizin oluşturucuya sözdizimi base[E] kullanılarak erişilir; burada E virgülle ayrılmış bir ifade listesi şeklindedir.
  • "Otomatik olarak uygulanan dizin oluşturucu" kavramı yoktur. accessor_body'leri noktalı virgülle kullanan, soyut olmayan ve dış olmayan bir dizinleyiciye sahip olmak hatadır.

Bu farklılıkların yanı sıra , §15.7.3, §15.7.5 ve §15.7.6'da tanımlanan tüm kurallar dizin oluşturucu erişimcileri ve özellik erişimcileri için geçerlidir.

Bu özellik/özelliklerin §15.7.3, §15.7.5 ve §15.7.6 okunurken dizin oluşturucu/dizin oluşturucularla değiştirilmesi tanımlı terimler için de geçerlidir. Özellikle, okuma-yazma özelliği, okuma-yazma dizin oluşturucuya; salt okunur özellik, salt okunur dizin oluşturucuya; ve salt yazma özelliği ise salt yazma dizin oluşturucusuna dönüşür.

15.10 İşleçleri

15.10.1 Genel

İşleç, sınıfın örneklerine uygulanabilen bir ifade işlecinin anlamını tanımlayan bir üyedir. İşleçler operator_declarationkullanılarak bildirilir:

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | unsafe_modifier   // unsafe code support
    ;

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
    ;

unary_operator_declarator
    : type 'operator' overloadable_unary_operator '(' fixed_parameter ')'
    ;

logical_negation_operator
    : '!'
    ;

overloadable_unary_operator
    : '+' | '-' | logical_negation_operator | '~' | '++' | '--' | 'true' | 'false'
    ;

binary_operator_declarator
    : type 'operator' overloadable_binary_operator
        '(' fixed_parameter ',' fixed_parameter ')'
    ;

overloadable_binary_operator
    : '+'  | '-'  | '*'  | '/'  | '%'  | '&' | '|' | '^'  | '<<' 
    | right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
    ;

conversion_operator_declarator
    : 'implicit' 'operator' type '(' fixed_parameter ')'
    | 'explicit' 'operator' type '(' fixed_parameter ')'
    ;

operator_body
    : block
    | '=>' expression ';'
    | ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

Not: Ön ek mantıksal olumsuzlaması (§12.9.4) ve sonek null-forgiving işleçleri (§12.8.9), aynı sözcük belirteci ()! ile temsil edilirken farklıdır. İkincisi aşırı yüklenebilir bir işleç değildir. son not

Üç aşırı yüklenebilir işleç kategorisi vardır: Birli işleçler (§15.10.2), ikili işleçler (§15.10.3) ve dönüştürme işleçleri (§15.10.4).

operator_body noktalı virgül, blok gövdesi (§15.6.1) veya ifade gövdesidir (§15.6.1). Bir blok gövdesi, işleç çağrıldığında yürütülecek deyimleri tanımlayan bir blok içerir. Blok, §15.6.11'de açıklanan değer döndüren yöntemlerle ilgili kurallara uygun olacaktır. İfade gövdesi, ardından bir ifade ve noktalı virgülden oluşur => ve işleç çağrıldığında gerçekleştirilecek tek bir ifadeyi belirtir.

extern işleçleri için operator_body yalnızca bir noktalı virgülden oluşur. Diğer tüm işleçler için operator_body bir blok gövdesi veya ifade gövdesidir.

Aşağıdaki kurallar tüm işleç bildirimleri için geçerlidir:

  • Bir işleç bildirimi hem bir public hem de bir static değiştirici içermelidir.
  • Bir işlecin parametrelerinde in dışında başka değiştiriciler bulunmamalıdır.
  • Bir işlecin (§15.10.2, §15.10.3, §15.10.4) imzası, aynı sınıfta beyan edilen diğer tüm işleçlerin imzalarından farklı olmalıdır.
  • Bir işleç bildiriminde başvuruda bulunılan tüm türler en az operatörün kendisi kadar erişilebilir olmalıdır (§7.5.5).
  • Aynı değiştiricinin bir işleç bildiriminde birden çok kez görünmesi bir hatadır.

Her işleç kategorisi, aşağıdaki alt kısıtlamalarda açıklandığı gibi ek kısıtlamalar uygular.

Diğer üyeler gibi, bir temel sınıfta bildirilen işleçler türetilmiş sınıflar tarafından devralınır. İşleç bildirimleri her zaman işlecin işlecin imzasına katılmak üzere bildirildiği sınıf veya yapıyı gerektirdiğinden, türetilmiş bir sınıfta bildirilen bir işlecin temel sınıfta bildirilen bir işleci gizlemesi mümkün değildir. Bu nedenle, new değiştirici bir işleç bildiriminde hiçbir zaman gerekli değildir ve bu nedenle hiçbir zaman izin verilmez.

Birli ve ikili işleçler hakkında ek bilgileri §12.4'te bulabilirsiniz.

Dönüştürme işleçleri hakkında ek bilgileri §10.5'te bulabilirsiniz.

15.10.2 Tekli operatörler

Aşağıdaki kurallar, T işleç bildirimini içeren sınıfın veya yapının örnek türünü belirten birli işleç bildirimleri için geçerlidir:

  • Bir unary +, -, ! (yalnızca mantıksal olumsuzlama) veya ~ işleci, T veya T? türünde tek bir parametre almalı ve herhangi bir tür döndürebilmelidir.
  • Bir birli ++ veya -- işleç, tek bir tür T parametresini alacaktır veya T? aynı türde veya ondan türetilmiş bir tür döndürecektir.
  • Birlikli true veya false işleci, türünde T veya T? tek bir parametre alacaktır ve türü bool döndürecektir.

Birli işlecin imzası, işleç belirtecini (+, -, !, , ~, ++, --veya truefalse) ve tek parametrenin türünden oluşur. Dönüş türü, birli işlecin imzasının bir parçası değildir ve parametrenin adı değildir.

true ve false tekli işleçler çifte bildirim gerektirir. Bir sınıf, diğerini de bildirmeden bu işleçlerden birini bildirirse derleme zamanı hatası oluşur. true ve false işleçleri §12.26'da daha ayrıntılı olarak açıklanmıştır.

Örnek: Aşağıdaki örnek, bir tamsayı vektör sınıfı için operator++ uygulamasını ve sonraki kullanımını gösterir:

public class IntVector
{
    public IntVector(int length) {...}
    public int Length { get { ... } }                      // Read-only property
    public int this[int index] { get { ... } set { ... } } // Read-write indexer

    public static IntVector operator++(IntVector iv)
    {
        IntVector temp = new IntVector(iv.Length);
        for (int i = 0; i < iv.Length; i++)
        {
            temp[i] = iv[i] + 1;
        }
        return temp;
    }
}

class Test
{
    static void Main()
    {
        IntVector iv1 = new IntVector(4); // Vector of 4 x 0
        IntVector iv2;
        iv2 = iv1++;              // iv2 contains 4 x 0, iv1 contains 4 x 1
        iv2 = ++iv1;              // iv2 contains 4 x 2, iv1 contains 4 x 2
    }
}

İşleç yönteminin, sonek artırma ve azaltma işleçleri (§12.8.16) ve ön ek artırma ve azaltma işleçleri (§12.9.7) gibi işlenene 1 ekleyerek üretilen değeri nasıl döndürdüğüne dikkat edin. C++'dan farklı olarak, bu yöntem işleneninin değerini doğrudan değiştirmemelidir, çünkü bu durum sonek artış operatörünün (§12.8.16) standart anlamını ihlal eder.

son örnek

15.10.3 İkili işleçler

Aşağıdaki kurallar, T işleç bildirimini içeren sınıfın veya yapının örnek türünü belirten ikili işleç bildirimleri için geçerlidir:

  • İkiye bölünmüş bir vardiya operatörü, en az biri T veya T? türünde olan iki parametre alır ve herhangi bir türü döndürebilir.
  • İkili << veya >> işleç (§12.13) iki parametre alacaktır; bunlardan ilki türü T veya T? ikincisi veya inttürüne int? sahip olacak ve herhangi bir türü döndürebilir.

İkili işlecin imzası, işleç belirtecini (+, -, *, /, %, &, |^<<>>==!=, , ><>=veya <=) ve iki parametrenin türlerinden oluşur. Dönüş türü ve parametrelerin adları ikili işlecin imzasının bir parçası değildir.

Bazı ikili işleçler çift tabanlı bildirim gerektirir. Bir çiftin her iki işlecinin her bildirimi için, çiftin diğer işlecinin eşleşen bir bildirimi olacaktır. İki operatör bildirimi, dönüş türleri ve karşılık gelen parametre türleri arasında kimlik dönüştürmeleri mevcutsa eşleşir. Aşağıdaki işleçler çift yönlü bildirim gerektirir:

  • işleç == ve işleç !=
  • işleç > ve işleç <
  • işleç >= ve işleç <=

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

Dönüştürme işleci bildirimi, önceden tanımlanmış örtük ve açık dönüştürmeleri genişleten kullanıcı tanımlı bir dönüştürme (§10.5) sağlar.

anahtar sözcüğünü implicit içeren bir dönüştürme işleci bildirimi, kullanıcı tanımlı örtük dönüştürmeyi tanıtır. Örtük dönüştürmeler işlev üyesi çağrıları, atama ifadeleri ve atamalar gibi çeşitli durumlarda oluşabilir. Bu, §10.2'de daha ayrıntılı olarak açıklanmıştır.

anahtar sözcüğünü explicit içeren bir dönüştürme işleci bildirimi, kullanıcı tanımlı açık dönüştürmeyi tanıtır. Açık dönüştürmeler dönüştürme ifadelerinde gerçekleşebilir ve §10.3'te daha ayrıntılı olarak açıklanmıştır.

Dönüştürme işleci, dönüştürme işlecinin parametre türüyle belirtilen bir kaynak türünden, dönüştürme işlecinin dönüş türüyle belirtilen hedef türe dönüştürür.

Belirli bir kaynak türü S ve hedef türü T için, eğer S veya T null atanabilir değer türleriyse, S₀ ve T₀ temel türlerine başvurur; aksi takdirde, S₀ ve T₀ sırasıyla S ve T'e eşittir. Bir sınıfın veya yapının, yalnızca aşağıdakilerin tümü doğru olduğunda kaynak türden S hedef türe T dönüştürme bildirmesine izin verilir:

  • S₀ ve T₀ farklı türlerdir.

  • veya S₀T₀ işleç bildirimini içeren sınıfın veya yapının örnek türüdür.

  • Ne S₀ ne de T₀ bir interface_typetir.

  • Kullanıcı tanımlı dönüştürmeler hariç, S'den T'e veya T'den S'e dönüştürme yoktur.

Bu kurallara göre, S veya T ile ilişkilendirilmiş tüm tür parametreleri, diğer türlerle kalıtım ilişkisi olmayan benzersiz türler olarak kabul edilir ve bu tür parametrelerindeki kısıtlamalar göz ardı edilir.

Örnek: Aşağıdakilerde:

class C<T> {...}

class D<T> : C<T>
{
    public static implicit operator C<int>(D<T> value) {...}     // Ok
    public static implicit operator C<string>(D<T> value) {...}  // Ok
    public static implicit operator C<T>(D<T> value) {...}       // Error
}

ilk iki işleç bildirimine izin verilir çünkü T ve int ve string sırasıyla ilişkisiz benzersiz türler olarak kabul edilir. Ancak, üçüncü işleç bir hatadır çünkü C<T> temel sınıfıdır D<T>.

son örnek

İkinci kuraldan, bir dönüştürücü işlecin, bildirildiği sınıf veya yapı türüne ya da bu türden dönüştürmesi gerektiği sonucu çıkar.

Örnek: Bir sınıf veya yapı türünün C'den C'e ve int'den int'e dönüştürme tanımlaması mümkündür, ancak C'dan int'ye yapılamaz. son örnek

Önceden tanımlanmış bir dönüştürmeyi doğrudan yeniden tanımlamak mümkün değildir. Bu nedenle, object ve diğer tüm türler arasında örtük ve açık dönüştürmeler zaten mevcut olduğundan, dönüştürme işleçlerinin object'ye dönüştürmesine veya object'den dönüştürmesine izin verilmez. Benzer şekilde, bir dönüştürmenin ne kaynak ne de hedef türü diğerinin temel türü olamaz, çünkü bu durumda zaten bir dönüştürme mevcut olurdu. Ancak, belirli tür bağımsız değişkenleri için önceden tanımlanmış dönüştürmeler olarak zaten var olan dönüştürmeleri belirten genel türlerde işleçler bildirmek mümkündür .

Örnek:

struct Convertible<T>
{
    public static implicit operator Convertible<T>(T value) {...}
    public static explicit operator T(Convertible<T> value) {...}
}

Türü object için T bir tür bağımsız değişkeni olarak belirtildiğinde, ikinci işleç zaten var olan bir dönüşümü tanımlar (örtük (dolayısıyla açık da) bir dönüşüm, herhangi bir türden object türüne vardır).

son örnek

İki tür arasında önceden tanımlanmış bir dönüştürmenin mevcut olduğu durumlarda, bu türler arasındaki kullanıcı tanımlı dönüştürmeler yoksayılır. Özellikle:

  • Tür 'den tür S'ye önceden tanımlanmış bir örtük dönüştürme (T) varsa, tür S'den tür T'ye tüm kullanıcı tanımlı dönüştürmeler (örtük veya açık) yoksayılır.
  • Eğer türünden S türüne önceden tanımlanmış bir açık dönüştürme (T) varsa, S'den T'ye yapılan kullanıcı tanımlı açık dönüştürmeler yoksayılır. Ayrıca:
    • S veya T bir arabirim türüyse, S'den T'e kullanıcı tarafından tanımlanan örtük dönüşümler yoksayılır.
    • Aksi takdirde, S'dan T'e kullanıcı tanımlı örtük dönüştürmeler yine de dikkate alınır.

dışında objecttüm türler için, yukarıdaki tür tarafından Convertible<T> bildirilen işleçler önceden tanımlanmış dönüştürmelerle çakışmaz.

Örnek:

void F(int i, Convertible<int> n)
{
    i = n;                    // Error
    i = (int)n;               // User-defined explicit conversion
    n = i;                    // User-defined implicit conversion
    n = (Convertible<int>)i;  // User-defined implicit conversion
}

Ancak, önceden tanımlanmış dönüştürmeler, türü object için, bir durum dışında tüm kullanıcı tanımlı dönüştürmeleri gizler.

void F(object o, Convertible<object> n)
{
    o = n;                       // Pre-defined boxing conversion
    o = (object)n;               // Pre-defined boxing conversion
    n = o;                       // User-defined implicit conversion
    n = (Convertible<object>)o;  // Pre-defined unboxing conversion
}

son örnek

Kullanıcı tanımlı dönüştürmeler ile interface_type'lere veya interface_type'lerden dönüştürme yapılamaz. Özellikle, bu kısıtlama bir interface_type dönüştürürken kullanıcı tanımlı dönüştürmelerin gerçekleşmemesini ve bir interface_type dönüştürmenin object yalnızca dönüştürülen öğe gerçekten belirtilen interface_type uyguladığında başarılı olmasını sağlar.

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.

Not: Genel olarak, kullanıcı tanımlı örtük dönüştürmeler hiçbir zaman özel durum oluşturmamak ve hiçbir zaman bilgi kaybetmemek için tasarlanmalıdır. Kullanıcı tanımlı bir dönüştürme istisnalara (örneğin, kaynak bağımsız değişkeni aralık dışında olduğundan) veya bilgi kaybına (örneğin, yüksek değerlikli bitlerin ihmal edilmesi) neden olabiliyorsa, bu dönüşüm bir açık dönüştürme olarak tanımlanmalıdır. son not

Örnek: Aşağıdaki kodda

public struct Digit
{
    byte value;

    public Digit(byte value)
    {
        if (value < 0 || value > 9)
        {
            throw new ArgumentException();
        }
        this.value = value;
    }

    public static implicit operator byte(Digit d) => d.value;
    public static explicit operator Digit(byte b) => new Digit(b);
}

Digit byte dönüştürmesi örtük bir işlemdir çünkü hiçbir zaman özel durumlar oluşturmaz veya bilgi kaybetmez, ancak byteDigit dönüştürme açıktır çünkü Digit için dönüştürme işlemi, olası değerlerinin byteyalnızca bir alt kümesini temsil edebilir.

son örnek

15.11 Örnek oluşturucuları

15.11.1 Genel

Örnek oluşturucu, bir sınıfın örneğini başlatmak için gereken eylemleri uygulayan bir üyedir. Örnek oluşturucuları constructor_declarationkullanılarak bildirilir:

constructor_declaration
    : attributes? constructor_modifier* constructor_declarator constructor_body
    ;

constructor_modifier
    : 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'extern'
    | unsafe_modifier   // unsafe code support
    ;

constructor_declarator
    : identifier '(' parameter_list? ')' constructor_initializer?
    ;

constructor_initializer
    : ':' 'base' '(' argument_list? ')'
    | ':' 'this' '(' argument_list? ')'
    ;

constructor_body
    : block
    | '=>' expression ';'
    | ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

constructor_declaration bir öznitelik kümesi (§23), izin verilen bildirilen erişilebilirlik türlerinden herhangi biri (§15.3.6) ve bir extern (§15.6.8) değiştirici içerebilir. Oluşturucu bildiriminin aynı değiştiriciyi birden çok kez eklemesine izin verilmez.

bir constructor_declarator tanımlayıcısı, örnek oluşturucusunun bildirildiği sınıfı adlandıracaktır. Başka bir ad belirtilirse, derleme zamanı hatası oluşur.

Örnek oluşturucusunun isteğe bağlı parameter_list , bir yöntemin parameter_list (§15.6) ile aynı kurallara tabidir. Parametrelerin this değiştiricisi yalnızca uzantı yöntemlerine (§15.6.10) uygulandığından, oluşturucunun parameter_list hiçbir parametre değiştiriciyi this içermeyecektir. Parametre listesi bir örnek oluşturucusunun imzasını (§7.6) tanımlar ve aşırı yükleme çözümlemesinin (§12.6.4) bir çağrıda belirli bir örnek oluşturucuyu seçtiği işlemi yönetir.

Bir örnek oluşturucusunun parameter_list başvuruda bulunan türlerin her biri en az oluşturucunun kendisi kadar erişilebilir olmalıdır (§7.5.5).

İsteğe bağlı constructor_initializer, bu örnek oluşturucusunun constructor_body verilen deyimleri yürütmeden önce çağrılacak başka bir örnek oluşturucu belirtir. Bu, §15.11.2'de daha ayrıntılı olarak açıklanmıştır.

Oluşturucu bildirimi bir extern değiştirici içerdiğinde, oluşturucunun bir dış oluşturucu olduğu söylenir. Dış oluşturucu bildirimi gerçek bir uygulama sağlamadığından , constructor_body noktalı virgülden oluşur. Diğer tüm oluşturucular için constructor_body şunlardan oluşur:

  • sınıfının yeni bir örneğini başlatmak için deyimleri belirten bir blok; veya
  • bir ifade gövdesi, ardından bir => ve noktalı virgülden oluşur ve sınıfın yeni bir örneğini başlatmak için tek bir ifadeyi belirtir.

Blok veya ifade gövdesi olan bir constructor_body, dönüş türüne (§15.6.11) sahip bir void örnek yönteminin bloğuna tam olarak karşılık gelir.

Örnek oluşturucuları devralınmıyor. Bu nedenle, bir sınıfın örnek oluşturucu bildirimleri içermemesi durumunda varsayılan bir örnek oluşturucusunun otomatik olarak sağlanması (§15.11.5) dışında, sınıfta bildirilenlerden başka örnek oluşturucuları yoktur.

Örnek oluşturucuları object_creation_expression tarafından (§12.8.17.2) ve constructor_initializeraracılığıyla çağrılır.

15.11.2 Oluşturucu başlatıcıları

Tüm örnek oluşturucuları (sınıf objecthariç) örtük olarak constructor_body hemen önce başka bir örnek oluşturucunun çağrısını içerir. Örtük olarak çağrılacak oluşturucu, constructor_initializer tarafından belirlenir:

  • Form base(argument_list) (burada argument_list isteğe bağlıdır), doğrudan temel sınıftan bir örnek oluşturucunun çağrılmasına neden olur. Bu oluşturucu argument_list ve §12.6.4 aşırı yükleme çözümleme kuralları kullanılarak seçilir. Aday örnek oluşturucuları kümesi, doğrudan temel sınıfın tüm erişilebilir örnek oluşturucularından oluşur. Bu küme boşsa veya tek bir en iyi örnek oluşturucu tanımlanamıyorsa derleme zamanı hatası oluşur.
  • Formu this(argument_list) (burada argument_list isteğe bağlıdır) gibi olan bir örnek oluşturucu başlatıcı, aynı sınıftan başka bir örnek oluşturucu çağırır. Oluşturucu argument_list ve §12.6.4 aşırı yükleme çözümleme kuralları kullanılarak seçilir. Aday örnek oluşturucuları kümesi, sınıfın kendisinde bildirilen tüm örnek oluşturucularından oluşur. Sonuçta elde edilen geçerli örnek oluşturucu kümesi boşsa veya tek bir en iyi örnek oluşturucu tanımlanamıyorsa, derleme zamanı hatası oluşur. Bir örnek oluşturucu bildirimi kendisini bir veya daha fazla oluşturucu başlatıcısı zinciri aracılığıyla çağırırsa, derleme zamanı hatası oluşur.

Örnek yapıcısının bir yapıcı başlatıcısı yoksa, base() biçiminde bir yapıcı başlatıcı varsayılan olarak sağlanır.

Not: Bu nedenle, formun örnek oluşturucu bildirimi

C(...) {...}

tam anlamıyla eşdeğer

C(...) : base() {...}

son not

Bir örnek oluşturucu bildiriminin parameter_list tarafından verilen parametrelerin kapsamı, bu bildirimin oluşturucu başlatıcısını içerir. Bu nedenle, oluşturucu başlatıcısının oluşturucunun parametrelerine erişmesine izin verilir.

Örnek:

class A
{
    public A(int x, int y) {}
}

class B: A
{
    public B(int x, int y) : base(x + y, x - y) {}
}

son örnek

Örnek oluşturucu başlatıcısı, oluşturulan örneğe erişemiyor. Bu nedenle, bir bağımsız değişken ifadesinin bir simple_name aracılığıyla herhangi bir örnek üyesine başvurması bir derleme zamanı hatası olduğundan, oluşturucu başlatıcısının bağımsız değişken ifadesinde buna başvurmak derleme zamanı hatasıdır.

15.11.3 Örnek değişkeni başlatıcıları

Extern olmayan bir örnek oluşturucunun oluşturucu başlatıcısı yoksa veya biçiminde base(...)bir oluşturucu başlatıcısı varsa, bu oluşturucu, sınıfında bildirilen örnek alanlarının variable_initializertarafından belirtilen başlatmaları örtük olarak gerçekleştirir. Bu, oluşturucuya girdikten hemen sonra ve doğrudan temel sınıf oluşturucusunun örtük çağrılmadan önce yürütülen atama dizisine karşılık gelir. Değişken başlatıcıları, sınıf bildiriminde (§15.5.6) göründükleri metin sırasına göre yürütülür.

Değişken başlatıcıların extern örnek oluşturucuları tarafından yürütülmesi gerekmez.

15.11.4 Oluşturucu yürütme

Değişken başlatıcılar atama deyimlerine dönüştürülür ve bu atama deyimleri temel sınıf örneği oluşturucusunun çağrılmasından önce yürütülür. Bu sıralama, söz konusu örneğe erişimi olan tüm deyimler yürütülmeden önce tüm örnek alanlarının değişken başlatıcıları tarafından başlatılmasını sağlar.

Örnek: Aşağıdakiler göz önünde bulundurulduğunda:

class A
{
    public A()
    {
        PrintFields();
    }

    public virtual void PrintFields() {}
}
class B: A
{
    int x = 1;
    int y;

    public B()
    {
        y = -1;
    }

    public override void PrintFields() =>
        Console.WriteLine($"x = {x}, y = {y}");
}

B() yeni B örneği oluşturulduğunda, aşağıdaki sonuç üretilir:

x = 1, y = 0

Değişken başlatıcısı temel sınıf örneği oluşturucu çağrılmadan önce yürütülür çünkü değeri x 1'dir. Ancak, değeri y 0'dır (varsayılan değeri) intçünkü ataması y temel sınıf oluşturucu döndürene kadar yürütülmediğinden. Örnek değişken başlatıcılarını ve oluşturucu başlatıcılarını, constructor_body bölümünden önce otomatik olarak eklenen ifadeler olarak düşünmek yararlıdır. Örnek

class A
{
    int x = 1, y = -1, count;

    public A()
    {
        count = 0;
    }

    public A(int n)
    {
        count = n;
    }
}

class B : A
{
    double sqrt2 = Math.Sqrt(2.0);
    ArrayList items = new ArrayList(100);
    int max;

    public B(): this(100)
    {
        items.Add("default");
    }

    public B(int n) : base(n - 1)
    {
        max = n;
    }
}

çeşitli değişken başlatıcılar içerir; ayrıca her iki formun (base ve this) oluşturucu başlatıcılarını da içerir. Örnek, aşağıda gösterilen koda karşılık gelir; burada her açıklama otomatik olarak eklenen bir deyimi gösterir (otomatik olarak eklenen oluşturucu çağrıları için kullanılan söz dizimi geçerli değildir, ancak yalnızca mekanizmayı göstermek için kullanılır).

class A
{
    int x, y, count;
    public A()
    {
        x = 1;      // Variable initializer
        y = -1;     // Variable initializer
        object();   // Invoke object() constructor
        count = 0;
    }

    public A(int n)
    {
        x = 1;      // Variable initializer
        y = -1;     // Variable initializer
        object();   // Invoke object() constructor
        count = n;
    }
}

class B : A
{
    double sqrt2;
    ArrayList items;
    int max;
    public B() : this(100)
    {
        B(100);                      // Invoke B(int) constructor
        items.Add("default");
    }

    public B(int n) : base(n - 1)
    {
        sqrt2 = Math.Sqrt(2.0);      // Variable initializer
        items = new ArrayList(100);  // Variable initializer
        A(n - 1);                    // Invoke A(int) constructor
        max = n;
    }
}

son örnek

15.11.5 Varsayılan oluşturucular

Bir sınıf örnek oluşturucu bildirimi içermiyorsa, varsayılan bir örnek oluşturucu otomatik olarak sağlanır. Bu varsayılan oluşturucu, doğrudan temel sınıfın bir oluşturucusunu, biçiminde base()bir oluşturucu başlatıcısı varmış gibi çağırır. Sınıf soyutsa, varsayılan oluşturucu için bildirilen erişilebilirlik korunur. Aksi takdirde, varsayılan oluşturucu için bildirilen erişilebilirlik geneldir.

Not: Bu nedenle, varsayılan oluşturucu her zaman formdadır

protected C(): base() {}

veya

public C(): base() {}

burada C sınıfın adıdır.

son not

Aşırı yükleme çözümlemesi, temel sınıf oluşturucu başlatıcısı için benzersiz bir en iyi adayı belirleyemezse derleme zamanı hatası oluşur.

Örnek: Aşağıdaki kodda

class Message
{
    object sender;
    string text;
}

sınıfı örnek oluşturucu bildirimleri içermediğinden varsayılan bir oluşturucu sağlanır. Bu nedenle, örnek tam olarak

class Message
{
    object sender;
    string text;

    public Message() : base() {}
}

son örnek

15.12 Statik oluşturucular

Statik oluşturucu , kapalı bir sınıfı başlatmak için gereken eylemleri uygulayan bir üyedir. Statik oluşturucular static_constructor_declarationkullanılarak bildirilir:

static_constructor_declaration
    : attributes? static_constructor_modifiers identifier '(' ')'
        static_constructor_body
    ;

static_constructor_modifiers
    : 'static'
    | 'static' 'extern' unsafe_modifier?
    | 'static' unsafe_modifier 'extern'?
    | 'extern' 'static' unsafe_modifier?
    | 'extern' unsafe_modifier 'static'
    | unsafe_modifier 'static' 'extern'?
    | unsafe_modifier 'extern' 'static'
    ;

static_constructor_body
    : block
    | '=>' expression ';'
    | ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

static_constructor_declaration bir öznitelik kümesi (§23) ve değiştirici extern (§15.6.8) içerebilir.

Bir static_constructor_declaration tanımlayıcısı, statik oluşturucunun bildirildiği sınıfı adlandırır. Başka bir ad belirtilirse, derleme zamanı hatası oluşur.

Statik oluşturucu bildirimi bir extern değiştirici içerdiğinde, statik oluşturucunun bir dış statik oluşturucu olduğu söylenir. Dış statik oluşturucu bildirimi gerçek bir uygulama sağlamadığından , static_constructor_body noktalı virgülden oluşur. Diğer tüm statik oluşturucu bildirimleri için static_constructor_body şunlardan oluşur:

  • sınıfını başlatmak için yürütülecek deyimleri belirten bir blok; veya
  • Bir ifade gövdesi, bir => ardından bir ifade ve bir noktalı virgül içeren, sınıfı başlatmak için yürütülecek tek bir ifadeyi belirtir.

Blok veya ifade gövdesi olan bir static_constructor_body, dönüş türüne (§15.6.11voidkarşılık gelir.

Statik oluşturucular devralınamaz ve doğrudan çağrılamaz.

Kapalı bir sınıf için statik oluşturucu, belirli bir uygulama etki alanında en fazla bir kez yürütülür. Statik oluşturucunun yürütülmesi, aşağıdaki olayların ilki tarafından bir uygulama etki alanında gerçekleşecek şekilde tetikleniyor:

  • Sınıfının bir örneği oluşturulur.
  • Sınıfın statik üyelerinden herhangi birine başvurulur.

Bir sınıf, yürütmenin başladığı Main yöntemini (§7.1) içeriyorsa, bu sınıf için statik oluşturucu, Main yöntem çağrılmadan önce yürütülür.

Yeni bir kapalı sınıf türü başlatmak için, önce bu kapalı tür için yeni bir statik alan kümesi (§15.5.2) oluşturulmalıdır. Statik alanların her biri varsayılan değerine (§15.5.5) başlatılmalıdır. Bunu takip edin:

  • Statik oluşturucu veya extern olmayan bir statik oluşturucu yoksa:
    • statik alan başlatıcıları (§15.5.6.2) bu statik alanlar için yürütülür;
    • varsa, extern olmayan statik oluşturucu yürütülür.
  • Aksi takdirde bir extern statik oluşturucu varsa yürütülür. Statik değişken başlatıcılarının extern statik oluşturucular tarafından yürütülmesi gerekmez.

Örnek: Örnek

class Test
{
    static void Main()
    {
        A.F();
        B.F();
    }
}

class A
{
    static A()
    {
        Console.WriteLine("Init A");
    }

    public static void F()
    {
        Console.WriteLine("A.F");
    }
}

class B
{
    static B()
    {
        Console.WriteLine("Init B");
    }

    public static void F()
    {
        Console.WriteLine("B.F");
    }
}

çıktıyı üretmelidir:

Init A
A.F
Init B
B.F

'nin statik oluşturucusunun Ayürütülmesi çağrısı A.Ftarafından tetiklendiğinden ve 'nin statik oluşturucusunun Byürütülmesi çağrısı B.Ftarafından tetiklendiğinden.

son örnek

Değişken başlatıcıları olan statik alanların varsayılan değer durumlarında gözlemlenmelerine olanak sağlayan döngüsel bağımlılıklar oluşturmak mümkündür.

Örnek: Örnek

class A
{
    public static int X;

    static A()
    {
        X = B.Y + 1;
    }
}

class B
{
    public static int Y = A.X + 1;

    static B() {}

    static void Main()
    {
        Console.WriteLine($"X = {A.X}, Y = {B.Y}");
    }
}

çıktıyı üretir

X = 1, Y = 2

Yöntemini yürütmek Main için, sistem önce sınıfının B.Ystatik oluşturucusunun öncesinde için Bbaşlatıcıyı çalıştırır. Y'nin başlatıcısı değerine başvurulacağından oluşturucusunun A çalıştırılmasına static neden olurA.X. Statik oluşturucu A, sırasıyla X değerini hesaplamak için devam eder ve bunu yaparken varsayılan değeri olan sıfırı Y getirir. A.X bu nedenle 1 olarak başlatılır. A'nin statik alan başlatıcıları ve statik oluşturucusunun çalıştırılması işlemi tamamlanır, ardından Y'in başlangıç değeri hesaplanır ve bu hesaplama sonucu 2 olur.

son örnek

Statik oluşturucu, her kapalı yapılandırılmış sınıf türü için yalnızca bir kez çalıştırıldığı için derleme zamanında kısıtlamalarla (§15.2.5) kontrol edilemeyen tür parametresinde çalışma zamanı kontrollerini uygulamak için uygun bir yerdir.

Örnek: Aşağıdaki tür, tür bağımsız değişkeninin bir enum olmasını zorunlu kılmak için statik bir oluşturucu kullanır:

class Gen<T> where T : struct
{
    static Gen()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("T must be an enum");
        }
    }
}

son örnek

15.13 Sonlandırıcılar

Not: Bu belirtimin önceki bir sürümünde artık "sonlandırıcı" olarak adlandırılan şeye "yıkıcı" adı verilirdi. Deneyim, "yıkıcı" teriminin karışıklığa neden olduğunu ve genellikle özellikle C++ bilen programcılar için yanlış beklentilere yol açtığını göstermiştir. C++'ta, yıkıcı belirli bir şekilde çağrılırken, C# dilinde sonlandırıcı aynı şekilde belirli bir şekilde çağrılmaz. C# dilinden belirleyici bir davranış elde etmek için kullanmalıdır Dispose. son not

Sonlandırıcı, bir sınıfın örneğini sonlandırmak için gereken eylemleri uygulayan bir üyedir. Sonlandırıcı, finalizer_declaration kullanılarak bildirilir.

finalizer_declaration
    : attributes? '~' identifier '(' ')' finalizer_body
    | attributes? 'extern' unsafe_modifier? '~' identifier '(' ')'
      finalizer_body
    | attributes? unsafe_modifier 'extern'? '~' identifier '(' ')'
      finalizer_body
    ;

finalizer_body
    : block
    | '=>' expression ';'
    | ';'
    ;

unsafe_modifier (§24.2) yalnızca güvenli olmayan kodda (§24) kullanılabilir.

finalizer_declaration bir öznitelik kümesi (§23) içerebilir.

Bir finalizer_declarator tanımlayıcısı, sonlandırıcının bildirildiği sınıfı adlandırır. Başka bir ad belirtilirse, derleme zamanı hatası oluşur.

Sonlandırıcı bildirimi bir extern değiştirici içerdiğinde, sonlandırıcının bir dış sonlandırıcı olduğu söylenir. Dış sonlandırıcı bildirimi gerçek bir uygulama sağlamadığından , finalizer_body noktalı virgülden oluşur. Diğer tüm sonlandırıcılar için finalizer_body şunlardan oluşur:

  • sınıfının bir örneğini sonlandırmak için yürütülecek deyimleri belirten bir blok.
  • veya bir ifade gövdesi, ardından bir => ve noktalı virgülden oluşur ve sınıfın bir örneğini sonlandırmak için yürütülecek tek bir ifadeyi belirtir.

finalizer_body, blok veya ifade gövdesi olarak, bir dönüş türüne sahip bir örnek yöntemin method_body ile tam olarak karşılık gelir (§15.6.11).

Sonlandırıcılar devralınmıyor. Bu nedenle, bir sınıfın, bu sınıfta bildirilebilenden başka sonlandırıcıları yoktur.

Not: Bir sonlandırıcının parametresi olmaması gerektiğinden, aşırı yüklenemez, bu nedenle bir sınıfın en fazla bir sonlandırıcısı olabilir. son not

Sonlandırıcılar otomatik olarak çağrılır ve açıkça çağrılamaz. Herhangi bir kodun bu örneği kullanması artık mümkün olmadığında bir örnek sonlandırma için uygun hale gelir. Örnek için sonlandırıcının yürütülmesi, örnek sonlandırma için uygun hale geldikten (§7.9) sonra herhangi bir zamanda gerçekleşebilir. Bir örnek sonlandırıldığında, söz konusu örneğin kalıtım zincirindeki sonlandırıcılar, en türetilmiş olandan en az türetilmiş olana doğru sırayla çağrılır. Sonlandırıcı herhangi bir iş parçacığı üzerinde yürütülebilir. Sonlandırıcının ne zaman ve nasıl yürütüldüğünü yöneten kurallar hakkında daha fazla bilgi için bkz . §7.9.

Örnek: Örneğin çıktısı

class A
{
    ~A()
    {
        Console.WriteLine("A's finalizer");
    }
}

class B : A
{
    ~B()
    {
        Console.WriteLine("B's finalizer");
    }
}

class Test
{
    static void Main()
    {
        B b = new B();
        b = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

%

B's finalizer
A's finalizer

devralma zincirindeki sonlandırıcılar, çoğu türetilmişten en az türetilene kadar sırayla çağrıldığından.

son örnek

Sonlandırıcılar, Finalize sanal yöntemi System.Object üzerinde geçersiz kılınarak uygulanır. C# programlarının bu yöntemi veya onun geçersiz kılmalarını doğrudan çağırmasına izin verilmez.

Örnek: Örneğin, program

class A
{
    override protected void Finalize() {}  // Error
    public void F()
    {
        this.Finalize();                   // Error
    }
}

iki hata içerir.

son örnek

Derleyici, bu yöntem ve onun geçersiz kılınmış halleri hiç yokmuş gibi davranmalıdır.

Örnek: Bu nedenle, bu program:

class A
{
    void Finalize() {}  // Permitted
}

geçerlidir ve gösterilen yöntem System.Object'nin Finalize yöntemini gizler.

son örnek

Sonlandırıcıdan özel durum oluştuğunda davranışla ilgili bir tartışma için bkz. §22.4.

15.14 Asenkron İşlevler

15.14.1 Genel

Değiştirici ile bir yöntem (§15.6), anonim işlev (§12.21) veya yerel işlev (async) zaman uyumsuz işlev olarak adlandırılır. Genel olarak, async terimi değiştiriciye sahip async herhangi bir işlev türünü tanımlamak için kullanılır.

Derleme zamanı hatası, zaman uyumsuz bir işlevin parametre listesinin, herhangi bir in, out, veya ref parametresi veya ref struct türdeki herhangi bir parametre belirtmesidir.

Bir zaman uyumsuz yöntemin return_type'ı ya void, bir zaman uyumsuz görev türü veya bir zaman uyumsuz yineleyici türü (§15.15) olmalıdır. Sonuç değeri üreten zaman uyumsuz bir yöntem için, bir görev türü veya zaman uyumsuz yineleyici türü (§15.15.3) genel olmalıdır. Sonuç değeri üretmeyen zaman uyumsuz bir yöntem için görev türü genel olmamalıdır. Bu türler, bu belirtimde sırasıyla «TaskType»<T> ve «TaskType» olarak adlandırılır. Standart kitaplık türü System.Threading.Tasks.Task ve System.Threading.Tasks.Task<TResult> ile System.Threading.Tasks.ValueTask<T>'den oluşturulmuş türler, özniteliği aracılığıyla bir ile ilişkilendirilmiş bir sınıf, yapı veya arabirim türü olan görev türleridir. Bu türler, bu belirtim içinde «TaskBuilderType»<T> ve «TaskBuilderType» olarak adlandırılır. Bir görev türü en fazla bir tür parametresine sahip olabilir ve genel türde iç içe yerleştirilemez.

Zamansız bir yöntem görev tipi döndürüyorsa, görev döndüren olarak adlandırılır.

Görev türleri tam tanımlarında farklılık gösterebilir, ancak dilin bakış açısından bir görev türü eksik, başarılı veya hatalı durumlardan birindedir. Hata meydana gelen bir görev ilgili istisnayı kaydeder. Başarılı bir «TaskType»<T> sonuç, T türünde kaydedilir. Görev türleri beklenebilir ve bu nedenle görevler await ifadelerinin işlenenleri olabilir (§12.9.9).

Örnek: Görev türü MyTask<T> , görev oluşturucu türü ve awaiter türüyle MyTaskMethodBuilder<T>Awaiter<T>ilişkilendirilir:

using System.Runtime.CompilerServices; 
[AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))]
class MyTask<T>
{
    public Awaiter<T> GetAwaiter() { ... }
}

class Awaiter<T> : INotifyCompletion
{
    public void OnCompleted(Action completion) { ... }
    public bool IsCompleted { get; }
    public T GetResult() { ... }
}

son örnek

Görev oluşturucu türü, belirli bir görev türüne (§15.14.2) karşılık gelen bir sınıf veya yapı türüdür. Görev oluşturucu türü, ilgili görev türünün bildirilen erişilebilirliğiyle tam olarak eşleşmelidir.

Not: Görev türü bildirilirse internal, ilgili oluşturucu türü de bildirilmeli internal ve aynı derlemede tanımlanmalıdır. Görev türü başka bir tür içinde iç içe yerleştirilmişse, görev oluşturucusu türü de aynı türde iç içe yerleştirilmelidir. son not

Zaman uyumsuz bir işlev, gövdesindeki await ifadeleri (§12.9.9) yoluyla değerlendirmeyi askıya alma özelliğine sahiptir. Değerlendirme daha sonra bir sürdürme temsilcisi yoluyla, askıya alınmış await ifadesinin noktasından devam ettirilebilir. Yeniden başlatma temsilcisi, türü System.Action olan bir temsilcidir ve çağrıldığında, zaman uyumsuz işlev çağrısının değerlendirmesi, kaldığı yerden await ifadesinden devam eder. Zaman uyumsuz işlev çağrısının geçerli çağıranı , işlev çağrısı hiçbir zaman askıya alınmadıysa özgün çağırandır veya aksi takdirde yeniden başlatma temsilcisinin en son çağıranıdır.

15.14.2 Görev türü oluşturucu düzeni

Bir görev oluşturucu türü en fazla bir tür parametresine sahip olabilir ve genel bir tür içinde iç içe yerleştirilemez. Bir görev oluşturucu türü, belirtilen SetResult erişilebilirliği olan aşağıdaki üyelere (genel olmayan görev oluşturucu türleri public için parametresiz) sahip olmalıdır:

class «TaskBuilderType»<T>
{
    public static «TaskBuilderType»<T> Create();
    public void Start<TStateMachine>(ref TStateMachine stateMachine)
                where TStateMachine : IAsyncStateMachine;
    public void SetStateMachine(IAsyncStateMachine stateMachine);
    public void SetException(Exception exception);
    public void SetResult(T result);
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine;
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine;
    public «TaskType»<T> Task { get; }
}

Derleyici, asin işlevin değerlendirilmesinin askıya alınması ve sürdürülmesi anlamını gerçekleştirmek için «TaskBuilderType» kullanan bir kod oluşturacaktır. Derleyici aşağıdaki gibi «TaskBuilderType» kullanacaktır:

  • «TaskBuilderType».Create() , bu listede adlı builder «TaskBuilderType» örneğini oluşturmak için çağrılır.
  • builder.Start(ref stateMachine) oluşturucuyu derleyici tarafından oluşturulan bir durum makinesi örneğiyle ilişkilendirmek için çağrılır stateMachine.
    • Oluşturucu, durum makinesini ilerletmek için ya stateMachine.MoveNext() içinde ya da Start() döndükten sonra Start() çağrısında bulunur.
  • Start() döndükten sonra, async yöntemi, asenkron yöntemden dönecek görevi tamamlamak için builder.Task'i çağırır.
  • Her bir stateMachine.MoveNext() çağrısı, durum makinesini ilerletecektir.
  • Durum makinesi başarıyla tamamlanırsa, builder.SetResult() yöntem dönüş değeriyle (varsa) çağrılır.
  • Aksi takdirde durum makinesinde e bir özel durum builder.SetException(e) oluşursa çağrılır.
  • await expr çağrılır, eğer durum makinesi bir expr.GetAwaiter() ifadeye ulaşırsa.
  • Awaiter, ICriticalNotifyCompletion uygularsa ve IsCompleted yanlış ise, durum makinesi builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine)'yi çağırır.
    • AwaitUnsafeOnCompleted() tamamlandığında awaiter.UnsafeOnCompleted(action) çağrısı yapacak bir Action ile stateMachine.MoveNext()'i çağırmalıdır.
  • Aksi takdirde, durum makinesi builder.AwaitOnCompleted(ref awaiter, ref stateMachine)'yi çağırır.
    • AwaitOnCompleted() tamamlandığında awaiter.OnCompleted(action) çağrısı yapacak bir Action ile stateMachine.MoveNext()'i çağırmalıdır.
  • SetStateMachine(IAsyncStateMachine) özellikle durum makinesinin bir değer türü olarak uygulandığı durumlarda, durum makinesi örneğiyle ilişkili oluşturucu örneğini tanımlamak için derleyici tarafından oluşturulan IAsyncStateMachine uygulama tarafından çağrılabilir.
    • Oluşturucu stateMachine.SetStateMachine(stateMachine) çağırırsa, stateMachine ile ilişkilendirilmiş builder.SetStateMachine(stateMachine) öğesini çağırırstateMachine.

Not: Her iki ve SetResult(T result) için, parametre ve bağımsız değişkenin kimliğe dönüştürülebilir olması gerekir «TaskType»<T> Task { get; }. Bu, görev türü oluşturucunun, farklı iki türün özdeş olarak dönüştürülebildiği tuples gibi türleri desteklemesine olanak tanır. son not

15.14.3 Görev döndüren asenkron işlevin değerlendirilmesi

Görev döndüren bir zaman uyumsuz işlevin çağrılması, döndürülen görev türünün bir örneğinin oluşturulmasına neden olur. Bu, zaman uyumsuz işlevin dönüş görevi olarak adlandırılır. Görev başlangıçta tamamlanmamış durumda.

Zaman uyumsuz işlev gövdesi askıya alınana (bir await ifadesine ulaşılarak) veya sonlandırılana kadar değerlendirilir; bu noktada denetim, dönüş göreviyle birlikte çağırana döndürülür.

Eşzamansız işlevin gövdesi tamamlandığında, dönüş görevi tamamlanmamış durumdan çıkarılır.

  • İşlev gövdesi bir dönüş deyimine veya gövdenin sonuna ulaşmanın sonucu olarak sonlandırılırsa, herhangi bir sonuç değeri dönüş görevine kaydedilir ve ardından bu görev başarılı durumuna geçer.
  • İşlev gövdesi, yakalanmayan bir OperationCanceledException nedeniyle sona ererse, istisna iptal edilen duruma getirilen dönüş görevine kaydedilir.
  • İşlev gövdesi başka bir yakalanmayan özel durumun (§13.10.6) sonucu olarak sonlandırılırsa, özel durum hatalı duruma getirilen dönüş görevine kaydedilir.

15.14.4 Boşluk döndüren bir zaman uyumsuz işlevin değerlendirilmesi

Zaman uyumsuz işlevin dönüş türü ise void, değerlendirme aşağıdaki şekilde yukarıdakilerden farklıdır: Hiçbir görev döndürülmediğinden, işlev bunun yerine tamamlanma ve özel durumları geçerli iş parçacığının eşitleme bağlamı iletir. Eşitleme bağlamının tam tanımı uygulamaya bağımlıdır, ancak geçerli iş parçacığının çalıştığı "where" ifadesinin bir gösterimidir. Eşitleme bağlamı, bir void döndüren asenkron bir işlevin değerlendirmesi başladığında, başarıyla tamamlandığında veya yakalanmamış bir özel durumun ortaya çıkmasına neden olduğunda bildirilir.

Bu, bağlamın altında kaç void-değer döndüren zaman uyumsuz işlevin çalıştığını takip etmesine ve bunlardan gelen istisnaları nasıl yayacağını belirlemesine olanak tanır.

15.15 Zaman uyumlu ve zaman uyumsuz yineleyiciler

15.15.1 Genel

Yineleyici bloğu (§13.3) kullanılarak uygulanan işlev üyesi (§12.6) veya yerel işlev (§13.6.4) yineleyici olarak adlandırılır. Yineleyici bloğu, karşılık gelen işlevin dönüş türü numaralandırıcı arabirimlerinden (§15.15.2) veya numaralandırılabilir arabirimlerden biri (§15.15.3) olduğu sürece işlevin gövdesi olarak kullanılabilir.

Yineleyici bloğu (§13.3) kullanılarak uygulanan zaman uyumsuz işlev (§15.14) veya yerel işlev (§13.6.4) zaman uyumsuz yineleyici olarak adlandırılır. Zaman uyumsuz yineleyici bloğu, karşılık gelen işlevin dönüş türü zaman uyumsuz numaralandırıcı arabirimi (§15.15.2) veya zaman uyumsuz numaralandırılabilir arabirim (§15.15.3) olduğu sürece işlevin gövdesi olarak kullanılabilir.

Yineleyici bloğu method_body, operator_body veya accessor_body olarak ortaya çıkabilirken olaylar, örnek oluşturucuları, statik oluşturucular ve sonlandırıcılar zaman uyumlu veya zaman uyumsuz yineleyici olarak uygulanmaz.

Bir işlev yineleyici bloğu kullanılarak uygulandığında, işlevin parametre listesinin herhangi inbir , outveya parametresini ya da ref bir tür parametresini belirtmesi bir derleme zamanı hatasıdır ref struct .

Zaman uyumsuz bir yineleyici, zaman uyumsuz işlemin iptalini destekleyecektir. Bu, §23.5.8'de açıklanmıştır.

15.15.2 Numaralandırıcı arabirimleri

Numaralandırıcı arabirimi, genel olmayan arabirim System.Collections.IEnumerator ve genel arabirimdirSystem.Collections.Generic.IEnumerator<T>.

Zaman uyumsuz numaralandırıcı arabirimi genel arabirimidirSystem.Collections.Generic.IAsyncEnumerator<T>.

Kısalık açısından, bu alt başlıkta ve eşdüzeylerinde bu arabirimlere sırasıyla IEnumerator, IEnumerator<T>, ve IAsyncEnumerator<T> olarak başvurulur.

15.15.3 Numaralandırılabilir arabirimler

Numaralandırılabilir arabirimler, genel olmayan arabirim System.Collections.IEnumerable ve genel arabirimlerdirSystem.Collections.Generic.IEnumerable<T>.

Zaman uyumsuz numaralandırılabilir arabirim genel arabirimidirSystem.Collections.Generic.IAsyncEnumerable<T>.

Kısalık açısından, bu alt başlıkta ve eşdüzeylerinde bu arabirimlere sırasıyla IEnumerable, IEnumerable<T>, ve IAsyncEnumerable<T> olarak başvurulur.

15.15.4 Verim türü

Yineleyici, tümü aynı türde bir değer dizisi oluşturur. Bu tür, yineleyicinin verim türü olarak adlandırılır.

  • IEnumerator veya IEnumerable döndüren bir yineleyicinin verim türü object olur.
  • IEnumerator<T>, IAsyncEnumerator<T>, IEnumerable<T> veya IAsyncEnumerable<T> döndüren bir yineleyicinin verim türü T'dır.

15.15.5 Numaralandırıcı nesneleri

15.15.5.1 Genel

Numaralandırıcı arabirim türü veya zaman uyumsuz numaralandırıcı arabirim türü döndüren bir işlev üyesi veya yerel işlev yineleyici bloğu kullanılarak uygulandığında, işlevi çağırmak kodu yineleyici bloğunda hemen yürütmez. Bunun yerine bir numaralandırıcı nesnesi oluşturulur ve döndürülür. Bu nesne yineleyici bloğunda belirtilen kodu kapsüller ve numaralandırıcı nesnesinin MoveNext veya MoveNextAsync yöntemi çağrıldığında yineleyici bloğundaki kodun yürütülmesi gerçekleşir. Numaralandırıcı nesnesi aşağıdaki özelliklere sahiptir:

  • System.IDisposable IEnumerator, ve IEnumerator<T>, veya System.IAsyncDisposable ve IAsyncEnumerator<T>uygular; burada T yineleyicinin verim türüdür.
  • Bağımsız değişken değerlerinin (varsa) bir kopyası ve işleve geçirilen örnek değeriyle başlatılır.
  • Dört olası durumu vardır: önce, çalışıyor, askıya alındı ve sonra, ve başlangıçta önce durumundadır.

Numaralandırıcı nesnesi genellikle yineleyici bloğundaki kodu kapsülleyen ve numaralandırıcı arabirimlerini uygulayan derleyici tarafından oluşturulan bir numaralandırıcı sınıfının örneğidir, ancak diğer uygulama yöntemleri mümkündür. Derleyici tarafından bir numaralandırıcı sınıfı oluşturulursa, bu sınıf doğrudan veya dolaylı olarak işlevini içeren sınıfta iç içe geçmiş olur, özel erişilebilirliği olur ve derleyici kullanımı için ayrılmış bir adı olur (§6.4.3).

Bir numaralandırıcı nesnesi, yukarıda belirtilenlerden daha fazla arabirim uygulayabilir.

Aşağıdaki alt bildirimler, numaralandırıcıyı ilerletmek, numaralandırıcıdan geçerli değeri almak ve numaralandırıcı tarafından kullanılan kaynakları atmak için üyenin gerekli davranışını açıklar. Bunlar sırasıyla zaman uyumlu ve zaman uyumsuz numaralandırıcılar için aşağıdaki üyelerde tanımlanır:

  • Numaralandırıcıyı ilerletmek için: MoveNext ve MoveNextAsync.
  • Geçerli değeri almak için: Current.
  • Kaynakları atmak için: Dispose ve DisposeAsync.

Numaralandırıcı nesneleri yöntemini desteklemez IEnumerator.Reset . Bu yöntem çağrılırsa bir System.NotSupportedException oluşturulur.

Eşzamanlı ve zaman uyumsuz yineleyici blokları, zaman uyumsuz yineleyici üyelerinin görev nesnelerini döndürmesi ve bu nesnelerin beklenebilmesi açısından farklıdır.

15.15.5.2 Numaralandırıcıyı ilerlet

MoveNext Bir numaralandırıcı nesnesinin ve MoveNextAsync yöntemleri, yineleyici bloğunun kodunu kapsüller. MoveNext veya MoveNextAsync yöntemini çağırmak, yineleyici bloğunda kod yürütür ve numaralandırıcı nesnesinin Current özelliğini uygun şekilde ayarlar.

MoveNext , anlamı aşağıda açıklanan bir bool değer döndürür. MoveNextAsync bir ValueTask<bool> (§15.14.3) döndürür. 'den MoveNextAsync döndürülen görevin sonuç değeri, içindeki MoveNextsonuç değeriyle aynı anlama sahiptir. Aşağıdaki açıklamada, MoveNext için açıklanan eylemler, MoveNextAsync için aşağıdaki farkla uygulanır: Eğer MoveNexttrue veya false döndürürse, MoveNextAsync görevi tamamlanmış duruma ayarlar ve görevin sonuç değerini ilgili true veya false değere ayarlar.

MoveNext veya MoveNextAsync tarafından gerçekleştirilen tam eylem, çağrıldığında numaralandırıcı nesnesinin durumuna bağlıdır.

  • Numaralandırıcı nesnesinin durumu daha önceyse, çağrılırMoveNext:
    • Durumu çalışır duruma değiştirir.
    • Yineleyici bloğunun parametrelerini ( this dahil) numaralandırıcı nesnesi başlatıldığında kaydedilen bağımsız değişken değerleri ve örnek değeriyle başlatır.
    • Yürütme kesilene kadar yineleyici bloğunu en baştan yürütür (aşağıda açıklandığı gibi).
  • Numaralandırıcı nesnesinin durumu çalışıyorsa, çağırmanın MoveNext sonucu belirtilmez.
  • Numaralandırıcı nesnesinin durumu askıya alınırsa MoveNext çağrılır:
    • Durumu çalışır duruma değiştirir.
    • Yineleyici bloğunun yürütülmesi en son askıya alınırken kaydedilen değerlere tüm yerel değişkenlerin ve parametrelerin (dahil this) değerlerini geri yükler.

      Not: Bu değişkenler tarafından başvuruda bulunan nesnelerin içeriği, önceki çağrısından MoveNextbu yana değişmiş olabilir. son not

    • Yürütmenin askıya alınmasına neden olan yield return ifadesinden hemen sonra yineleyici bloğunun yürütülmesine devam eder ve yürütme kesilene kadar sürer (aşağıda açıklandığı gibi).
  • Numaralandırıcı nesnesinin durumu sonra ise, MoveNext çağrıldığında false döndürür.

Yineleyici bloğu yürütülürken MoveNext , yürütme dört şekilde kesilebilir: Bir yield return deyimle, bir yield break deyimle, yineleyici bloğunun sonuyla karşılaşarak ve yineleyici bloğunun dışına oluşturulan ve yayılan bir özel durum tarafından.

Not: MoveNextAsync Tamamlanmamış bir görev türünü bekleyen bir await ifadeyi değerlendirirse askıya alınır. son not

  • Bir yield return deyimle karşılaşıldığında (§9.4.4.20):
    • deyiminde verilen ifade değerlendirilir, örtük olarak verim türüne dönüştürülür ve numaralandırıcı nesnesinin özelliğine Current atanır.
    • Yineleyici gövdesinin yürütülmesi askıya alınır. Tüm yerel değişkenlerin ve parametrelerin (dahil this) değerleri, bu yield return deyimin konumu gibi kaydedilir. yield return Deyimi bir veya daha fazla try blok içindeyse, ilişkili finally blokları şu anda yürütülmemiş olur.
    • Numaralandırıcı nesnesinin durumu askıya alınmış olarak değiştirilir.
    • MoveNext yöntemi çağıranı döndürür true ve yinelemenin bir sonraki değere başarıyla ilerlediğini gösterir.
  • Bir yield break deyimle karşılaşıldığında (§9.4.4.20):
    • yield break Deyimi bir veya daha fazla try blok içindeyse, ilişkili finally bloklar yürütülür.
    • Numaralandırıcı nesnesinin durumu sonra olarak değiştirilir.
    • MoveNext yöntemi, yinelemenin tamamlandığını belirten çağıranı döndürürfalse.
  • Yineleyici gövdesinin sonuyla karşılaşıldığında:
    • Numaralandırıcı nesnesinin durumu sonra olarak değiştirilir.
    • MoveNext yöntemi, yinelemenin tamamlandığını belirten çağıranı döndürürfalse.
  • Bir özel durum oluştuğunda ve yineleyici bloğundan yayıldığında:
    • Yineleyici gövdesindeki uygun finally bloklar özel durum yayma tarafından yürütülür.
    • Numaralandırıcı nesnesinin durumu sonra olarak değiştirilir.
    • Özel durum yayılması MoveNext yöntemini çağırana iletilir.

15.15.5.3 Geçerli değeri alma

Bir numaralandırıcı nesnesinin Current özelliği yineleyici bloğundaki deyimlerden etkilenir yield return .

Not: Current Özellik, hem zaman uyumlu hem de zaman uyumsuz yineleyici nesneleri için zaman uyumlu bir özelliktir. son not

Bir numaralandırıcı nesnesi askıya alınmış durumdayken, Current değeri önceki MoveNext tarafından çağrılan ayarlanan değerdir. Bir numaralandırıcı nesnesi öncesi, çalışıyor veya sonrası durumlarında olduğunda, Current erişiminin sonucu kesin değil.

"object dışında bir verim türüne sahip olan bir yineleyici için, numaralandırıcı nesnesinin Current uygulaması aracılığıyla IEnumerable'e erişmenin sonucu, numaralandırıcı nesnesinin Current uygulaması üzerinden IEnumerator<T>'e erişmeye ve sonucu object'a atamaya karşılık gelir."

15.15.5.4 Kaynakların Tasfiyesi

Dispose veya DisposeAsync yöntemi, numaralandırıcı nesnesini after durumuna getirerek yinelemeyi temizlemek için kullanılır.

  • Numaralandırıcı nesnesinin durumu önce ise, Dispose çağrıldığında durumu sonra olarak değiştirir.
  • Numaralandırıcı nesnesinin durumu çalışıyorsa, çağırmanın Dispose sonucu belirtilmez.
  • Numaralandırıcı nesnesinin durumu askıya alınırsa, çağrılırDispose:
    • Durumu çalışır duruma değiştirir.
    • Son yürütülen yield return deyimi bir yield break deyimiymiş gibi, tüm finally bloklarını yürütür. Bu, bir istisnanın atılmasına ve yineleyici gövdesinden dışarıya yayılmasına neden olursa, numaralandırıcı nesnesinin durumu after olarak ayarlanır ve istisna Dispose metodunu çağırana yayılır.
    • Durumu sonra olarak değiştirir.
  • Eğer numaralandırıcı nesnesinin durumu sonra ise, Dispose çağırmanın hiçbir etkisi olmaz.

15.15.6 Numaralandırılabilir nesneler

15.15.6.1 Genel

Numaralandırılabilir arabirim türü veya zaman uyumsuz numaralandırılabilir arabirim türü döndüren bir işlev üyesi veya yerel işlev yineleyici bloğu kullanılarak uygulandığında, işlevi çağırmak kodu yineleyici bloğunda hemen yürütmez. Bunun yerine, numaralandırılabilir bir nesne oluşturulur ve döndürülür.

Zaman uyumlu numaralandırılabilir nesne ve IEnumerableuygularIEnumerable<T>; burada T yineleyicinin verim türüdür. GetEnumerator Yöntemi bir numaralandırıcı nesnesi (§15.15.5) döndürür. Zaman uyumsuz bir numaralandırılabilir nesne uygular IAsyncEnumerable<T> ; burada T yineleyicinin verim türüdür. GetAsyncEnumerator Yöntemi zaman uyumsuz bir numaralandırıcı nesnesi (§15.15.5) döndürür.

Numaralandırılabilir bir nesne, bağımsız değişken değerlerinin (varsa) bir kopyası ve işleve geçirilen örnek değeriyle başlatılır.

Numaralandırılabilir nesne genellikle yineleyici bloğundaki kodu kapsülleyen ve numaralandırılabilir arabirimleri uygulayan derleyici tarafından oluşturulan numaralandırılabilir sınıfın bir örneğidir, ancak diğer uygulama yöntemleri mümkündür. Derleyici tarafından numaralandırılabilir bir sınıf oluşturulursa, bu sınıf işlevi içeren sınıfta doğrudan veya dolaylı olarak iç içe yerleştirilmiş olur, özel erişilebilirliği olur ve derleyici kullanımı için ayrılmış bir adı olur (§6.4.3).

Numaralandırılabilir bir nesne, yukarıda belirtilenlerden daha fazla arabirim uygulayabilir.

Not: Örneğin, numaralandırılabilir bir nesne ve IEnumeratoruygulayarak hem numaralandırılabilir hem de numaralandırıcı görevi görecek şekilde etkinleştirebilirIEnumerator<T>. Genellikle, böyle bir uygulama ilk çağrısından kendi örneğini (ayırmaları kaydetmek için GetEnumerator) döndürür. Varsa, GetEnumerator'ün sonraki çağrıları genellikle aynı sınıfın yeni bir örneğini döndürür, böylece farklı numaralandırıcı örneklerine yapılan çağrılar birbirini etkilemez. son not

15.15.6.2 GetEnumerator veya GetAsyncEnumerator yöntemi

Numaralandırılabilir nesne, ve GetEnumerator arabirimlerinin yöntemlerinin IEnumerable bir uygulamasını IEnumerable<T> sağlar. İki GetEnumerator yöntem, kullanılabilir bir numaralandırıcı nesnesi alan ve döndüren ortak bir uygulamayı paylaşır. Numaralandırıcı nesnesi, numaralandırılabilir nesne başlatıldığında kaydedilen bağımsız değişken değerleri ve örnek değeriyle başlatılır, ancak numaralandırıcı nesnesi §15.15.5'te açıklandığı gibi çalışır.

Zaman uyumsuz numaralandırılabilir nesne, GetAsyncEnumerator arabiriminin IAsyncEnumerable<T> yönteminin bir uygulamasını sağlar. Bu yöntem, kullanılabilir bir zaman uyumsuz numaralandırıcı nesnesi döndürür. Numaralandırıcı nesnesi, isteğe bağlı iptal belirteci dahil olmak üzere numaralandırılabilir nesne başlatıldığında kaydedilen bağımsız değişken değerleri ve örnek değeriyle başlatılır, ancak diğer durumlarda numaralandırıcı nesnesi §15.15.5'te açıklandığı gibi çalışır. Zaman uyumsuz yineleyici yöntemi (System.Runtime.CompilerServices.EnumeratorCancellationAttribute) kullanarak bir parametreyi iptal belirteci olarak işaretleyebilir. Bir uygulama, iptal belirteci (veya özniteliğiyle GetAsyncEnumeratorilişkilendirilen bağımsız değişken) iptal isteğinde bulunduğunda zaman uyumsuz bir yineleyicinin iptal edilmesi için iptal belirteçlerini birleştirmek için System.Runtime.CompilerServices.EnumeratorCancellationAttribute bir mekanizma sağlayacaktır.