İşlev IşaretçileriFunction Pointers

ÖzetSummary

Bu teklif, günümüzde C# ' de şu anda etkin şekilde erişilemeyen Il işlem kodları 'ları kullanan dil yapıları sağlar: ldftn ve calli .This proposal provides language constructs that expose IL opcodes that cannot currently be accessed efficiently, or at all, in C# today: ldftn and calli. Bu Il Opcode 'ları yüksek performans kodunda önemli olabilir ve geliştiricilerin bunlara erişmek için etkili bir yol olması gerekir.These IL opcodes can be important in high performance code and developers need an efficient way to access them.

MotivasyonMotivation

Bu özelliğin ilgi çekici ve arka planı, aşağıdaki konuda açıklanmaktadır (özelliğin olası bir uygulamasıdır):The motivations and background for this feature are described in the following issue (as is a potential implementation of the feature):

https://github.com/dotnet/csharplang/issues/191

Bu, derleyici iç bilgileri için alternatif bir tasarım teklifinde bulunurThis is an alternate design proposal to compiler intrinsics

Ayrıntılı tasarımDetailed Design

İşlev işaretçileriFunction pointers

Dil, sözdizimini kullanarak işlev işaretçilerinin bildirimine izin verir delegate* .The language will allow for the declaration of function pointers using the delegate* syntax. Tam söz dizimi, sonraki bölümde ayrıntılı olarak açıklanmıştır, ancak tarafından kullanılan sözdizimine Func ve tür bildirimlerine benzemesi amaçlanmıştır Action .The full syntax is described in detail in the next section but it is meant to resemble the syntax used by Func and Action type declarations.

unsafe class Example {
    void Example(Action<int> a, delegate*<int, void> f) {
        a(42);
        f(42);
    }
}

Bu türler, ECMA-335 ' de özetlenen işlev işaretçisi türü kullanılarak temsil edilir.These types are represented using the function pointer type as outlined in ECMA-335. Bu, bir öğesinin çağrılması delegate* calli delegate callvirt yönteminin yöntemi üzerinde kullanacağı yerde kullanacağı anlamına gelir Invoke .This means invocation of a delegate* will use calli where invocation of a delegate will use callvirt on the Invoke method. Sözdizimi, her iki yapı için de özdeş.Syntactically though invocation is identical for both constructs.

Yöntem işaretçilerinin ECMA-335 tanımı, tür imzasının bir parçası olarak çağırma kuralını içerir (Bölüm 7,1).The ECMA-335 definition of method pointers includes the calling convention as part of the type signature (section 7.1). Varsayılan çağırma kuralı olacaktır managed .The default calling convention will be managed. Yönetilmeyen çağırma kuralları, unmanaged delegate* çalışma zamanı platform varsayılanını kullanacak şekilde sözdizimi eklenerek bir anahtar sözcük eklenerek belirtilebilir.Unmanaged calling conventions can by specified by putting an unmanaged keyword afer the delegate* syntax, which will use the runtime platform default. Belirli yönetilmeyen kurallar, unmanaged ad alanında ile başlayan herhangi bir tür belirtilerek CallConv System.Runtime.CompilerServices , ön eke ayrılmadan anahtar sözcüğe parantez içinde belirtilebilir CallConv .Specific unmanaged conventions can then be specified in brackets to the unmanaged keyword by specifying any type starting with CallConv in the System.Runtime.CompilerServices namespace, leaving off the CallConv prefix. Bu türler programın çekirdek kitaplığından gelmelidir ve geçerli birleşimler kümesi platforma bağımlıdır.These types must come from the program's core library, and the set of valid combinations is platform-dependent.

//This method has a managed calling convention. This is the same as leaving the managed keyword off.
delegate* managed<int, int>;

// This method will be invoked using whatever the default unmanaged calling convention on the runtime
// platform is. This is platform and architecture dependent and is determined by the CLR at runtime.
delegate* unmanaged<int, int>;

// This method will be invoked using the cdecl calling convention
// Cdecl maps to System.Runtime.CompilerServices.CallConvCdecl
delegate* unmanaged[Cdecl] <int, int>;

// This method will be invoked using the stdcall calling convention, and suppresses GC transition
// Stdcall maps to System.Runtime.CompilerServices.CallConvStdcall
// SuppressGCTransition maps to System.Runtime.CompilerServices.CallConvSuppressGCTransition
delegate* unmanaged[Stdcall, SuppressGCTransition] <int, int>;

Türler arasındaki dönüştürmeler, delegate* çağırma kuralı dahil olmak üzere imzasına göre yapılır.Conversions between delegate* types is done based on their signature including the calling convention.

unsafe class Example {
    void Conversions() {
        delegate*<int, int, int> p1 = ...;
        delegate* managed<int, int, int> p2 = ...;
        delegate* unmanaged<int, int, int> p3 = ...;

        p1 = p2; // okay p1 and p2 have compatible signatures
        Console.WriteLine(p2 == p1); // True
        p2 = p3; // error: calling conventions are incompatible
    }
}

delegate*Tür, standart bir işaretçi türünün tüm özelliklerine ve kısıtlamalarına sahip olduğu anlamına gelen bir işaretçi türüdür:A delegate* type is a pointer type which means it has all of the capabilities and restrictions of a standard pointer type:

  • Yalnızca bir bağlamda geçerlidir unsafe .Only valid in an unsafe context.
  • Bir delegate* parametre veya dönüş türü içeren yöntemler yalnızca bir unsafe içerikten çağrılabilir.Methods which contain a delegate* parameter or return type can only be called from an unsafe context.
  • Öğesine dönüştürülemez object .Cannot be converted to object.
  • Genel bağımsız değişken olarak kullanılamaz.Cannot be used as a generic argument.
  • Örtük olarak delegate* öğesine dönüştürülebilir void* .Can implicitly convert delegate* to void*.
  • , ' Dan öğesine açıkça dönüştürülebilir void* delegate* .Can explicitly convert from void* to delegate*.

LarındanRestrictions:

  • Özel öznitelikler bir delegate* veya öğelerinden hiçbirine uygulanamaz.Custom attributes cannot be applied to a delegate* or any of its elements.
  • Bir delegate* parametre şu şekilde işaretlenemez paramsA delegate* parameter cannot be marked as params
  • Bir delegate* tür, normal bir işaretçi türünün tüm kısıtlamalarına sahiptir.A delegate* type has all of the restrictions of a normal pointer type.
  • İşaretçi aritmetiği doğrudan işlev işaretçisi türlerinde gerçekleştirilemez.Pointer arithmetic cannot be performed directly on function pointer types.

İşlev işaretçisi sözdizimiFunction pointer syntax

Tam işlev işaretçisi sözdizimi aşağıdaki dilbilgisinde temsil edilir:The full function pointer syntax is represented by the following grammar:

pointer_type
    : ...
    | funcptr_type
    ;

funcptr_type
    : 'delegate' '*' calling_convention_specifier? '<' funcptr_parameter_list funcptr_return_type '>'
    ;

calling_convention_specifier
    : 'managed'
    | 'unmanaged' ('[' unmanaged_calling_convention ']')?
    ;

unmanaged_calling_convention
    : 'Cdecl'
    | 'Stdcall'
    | 'Thiscall'
    | 'Fastcall'
    | identifier (',' identifier)*
    ;

funptr_parameter_list
    : (funcptr_parameter ',')*
    ;

funcptr_parameter
    : funcptr_parameter_modifier? type
    ;

funcptr_return_type
    : funcptr_return_modifier? return_type
    ;

funcptr_parameter_modifier
    : 'ref'
    | 'out'
    | 'in'
    ;

funcptr_return_modifier
    : 'ref'
    | 'ref readonly'
    ;

Hayır calling_convention_specifier sağlanmazsa, varsayılan olur managed .If no calling_convention_specifier is provided, the default is managed. ' Nin kesin meta veri kodlaması calling_convention_specifier ve identifier içinde geçerli olduğu, unmanaged_calling_convention çağırma kurallarının meta veri gösterimininkapsamına alınmıştır.The precise metadata encoding of the calling_convention_specifier and what identifiers are valid in the unmanaged_calling_convention is covered in Metadata Representation of Calling Conventions.

delegate int Func1(string s);
delegate Func1 Func2(Func1 f);

// Function pointer equivalent without calling convention
delegate*<string, int>;
delegate*<delegate*<string, int>, delegate*<string, int>>;

// Function pointer equivalent with calling convention
delegate* managed<string, int>;
delegate*<delegate* managed<string, int>, delegate*<string, int>>;

İşlev işaretçisi dönüştürmeleriFunction pointer conversions

Güvenli olmayan bir bağlamda, kullanılabilir örtük dönüştürmeler (örtük dönüştürmeler) kümesi aşağıdaki örtük işaretçi dönüşümlerini içerecek şekilde genişletilir:In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • Mevcut dönüşümlerExisting conversions
  • Funcptr _ türünden F0 başka bir funcptr _ türüne F1 , aşağıdakilerin tümü doğru olarak sağlandıysa:From funcptr_type F0 to another funcptr_type F1, provided all of the following are true:
    • F0``F1aynı sayıda parametreye sahiptir ve içindeki her bir parametre D0n F0 ref out in içindeki karşılık gelen parametresiyle aynı, veya değiştiricilere sahiptir D1n F1 .F0 and F1 have the same number of parameters, and each parameter D0n in F0 has the same ref, out, or in modifiers as the corresponding parameter D1n in F1.
    • Her bir değer parametresi (Hayır ref , veya değiştirici içeren bir parametre out ) için in , bir kimlik dönüştürme, örtük başvuru dönüştürme veya içindeki parametre türünden ' de karşılık gelen parametre türüne sahip örtülü işaretçi dönüştürme bulunur F0 F1 .For each value parameter (a parameter with no ref, out, or in modifier), an identity conversion, implicit reference conversion, or implicit pointer conversion exists from the parameter type in F0 to the corresponding parameter type in F1.
    • Her ref , out veya parametresi için in içindeki parametre türü, F0 içindeki karşılık gelen parametre türüyle aynıdır F1 .For each ref, out, or in parameter, the parameter type in F0 is the same as the corresponding parameter type in F1.
    • Dönüş türü değer (Hayır ref veya ref readonly ) ise, bir kimlik, örtük başvuru veya dönüş türünden dönüş türüne örtülü bir işaretçi dönüştürmesi vardır F1 F0 .If the return type is by value (no ref or ref readonly), an identity, implicit reference, or implicit pointer conversion exists from the return type of F1 to the return type of F0.
    • Dönüş türü başvuruya (veya) göre ise ref ref readonly , dönüş türü ve ref değiştiriciler, F1 dönüş türü ve ref değiştiricilerine benzer F0 .If the return type is by reference (ref or ref readonly), the return type and ref modifiers of F1 are the same as the return type and ref modifiers of F0.
    • Çağırma kuralı, F0 çağırma kuralıyla aynıdır F1 .The calling convention of F0 is the same as the calling convention of F1.

Hedef yöntemlere yönelik adrese izin verAllow address-of to target methods

Yöntem gruplarına artık bir adres ifadesi için bağımsız değişken olarak izin verilir.Method groups will now be allowed as arguments to an address-of expression. Böyle bir ifadenin türü, delegate* hedef yöntemin ve yönetilen bir çağrı kuralının denk imzasına sahip olan bir ifade olacaktır:The type of such an expression will be a delegate* which has the equivalent signature of the target method and a managed calling convention:

unsafe class Util {
    public static void Log() { }

    void Use() {
        delegate*<void> ptr1 = &Util.Log;

        // Error: type "delegate*<void>" not compatible with "delegate*<int>";
        delegate*<int> ptr2 = &Util.Log;
   }
}

Güvenli olmayan bir bağlamda, aşağıdakilerin tümü doğru ise bir yöntem bir M işlev işaretçisi türüyle uyumludur F :In an unsafe context, a method M is compatible with a function pointer type F if all of the following are true:

  • M``Faynı sayıda parametreye sahiptir ve içindeki her bir parametre M ref out in içindeki karşılık gelen parametresiyle aynı, veya değiştiricilere sahiptir F .M and F have the same number of parameters, and each parameter in M has the same ref, out, or in modifiers as the corresponding parameter in F.
  • Her bir değer parametresi (Hayır ref , veya değiştirici içeren bir parametre out ) için in , bir kimlik dönüştürme, örtük başvuru dönüştürme veya içindeki parametre türünden ' de karşılık gelen parametre türüne sahip örtülü işaretçi dönüştürme bulunur M F .For each value parameter (a parameter with no ref, out, or in modifier), an identity conversion, implicit reference conversion, or implicit pointer conversion exists from the parameter type in M to the corresponding parameter type in F.
  • Her ref , out veya parametresi için in içindeki parametre türü, M içindeki karşılık gelen parametre türüyle aynıdır F .For each ref, out, or in parameter, the parameter type in M is the same as the corresponding parameter type in F.
  • Dönüş türü değer (Hayır ref veya ref readonly ) ise, bir kimlik, örtük başvuru veya dönüş türünden dönüş türüne örtülü bir işaretçi dönüştürmesi vardır F M .If the return type is by value (no ref or ref readonly), an identity, implicit reference, or implicit pointer conversion exists from the return type of F to the return type of M.
  • Dönüş türü başvuruya (veya) göre ise ref ref readonly , dönüş türü ve ref değiştiriciler, F dönüş türü ve ref değiştiricilerine benzer M .If the return type is by reference (ref or ref readonly), the return type and ref modifiers of F are the same as the return type and ref modifiers of M.
  • Çağırma kuralı, M çağırma kuralıyla aynıdır F .The calling convention of M is the same as the calling convention of F. Bu hem çağırma kuralı bitini hem de yönetilmeyen tanımlayıcıda belirtilen tüm çağırma kuralı bayraklarını içerir.This includes both the calling convention bit, as well as any calling convention flags specified in the unmanaged identifier.
  • M statik bir yöntemdir.M is a static method.

Güvenli olmayan bir bağlamda, hedefi bir yöntem grubu olan bir adres ifadeden, E F Bu, E kendi normal biçiminde geçerli olan en az bir yöntemi F , aşağıdaki bölümünde açıklandığı gibi parametre türleri ve değiştiriciler kullanılarak oluşturulan bir bağımsız değişken listesine sahiptirIn an unsafe context, an implicit conversion exists from an address-of expression whose target is a method group E to a compatible function pointer type F if E contains at least one method that is applicable in its normal form to an argument list constructed by use of the parameter types and modifiers of F, as described in the following.

  • MAşağıdaki değişikliklerle formun yöntem çağrısına karşılık gelen tek bir yöntem seçilir E(A) :A single method M is selected corresponding to a method invocation of the form E(A) with the following modifications:
    • Bağımsız değişkenler listesi A , her biri bir değişken olarak sınıflandırılan ve ref out in karşılık gelen funcptr _ parametre _ listesinin türü ve değiştiricisi (,, veya) içeren bir ifadeler listesidir F .The arguments list A is a list of expressions, each classified as a variable and with the type and modifier (ref, out, or in) of the corresponding funcptr_parameter_list of F.
    • Aday Yöntemler, genişletilmiş biçiminde uygulananlar değil, yalnızca normal biçimlerinde geçerli olan yöntemlerdir.The candidate methods are only those methods that are applicable in their normal form, not those applicable in their expanded form.
    • Aday yöntemler yalnızca statik olan yöntemlerdir.The candidate methods are only those methods that are static.
  • Aşırı yükleme çözümlemesi algoritması bir hata üretirse, derleme zamanı hatası oluşur.If the algorithm of overload resolution produces an error, then a compile-time error occurs. Aksi takdirde, algoritma aynı sayıda parametreye sahip tek bir en iyi yöntem üretir M F ve dönüştürme var olarak kabul edilir.Otherwise, the algorithm produces a single best method M having the same number of parameters as F and the conversion is considered to exist.
  • Seçilen yöntem, M işlev işaretçisi türü ile uyumlu olmalıdır (yukarıda tanımlandığı gibi) F .The selected method M must be compatible (as defined above) with the function pointer type F. Aksi takdirde, bir derleme zamanı hatası oluşur.Otherwise, a compile-time error occurs.
  • Dönüştürmenin sonucu, türünde bir işlev işaretçisidir F .The result of the conversion is a function pointer of type F.

Bu, geliştiricilerin adres operatörü ile birlikte çalışmak için aşırı yükleme çözümleme kurallarına bağlı olabileceği anlamına gelir:This means developers can depend on overload resolution rules to work in conjunction with the address-of operator:

unsafe class Util {
    public static void Log() { }
    public static void Log(string p1) { }
    public static void Log(int i) { };

    void Use() {
        delegate*<void> a1 = &Log; // Log()
        delegate*<int, void> a2 = &Log; // Log(int i)

        // Error: ambiguous conversion from method group Log to "void*"
        void* v = &Log;
    }

Address-of işleci, yönergesi kullanılarak uygulanır ldftn .The address-of operator will be implemented using the ldftn instruction.

Bu özelliğin kısıtlamaları:Restrictions of this feature:

  • Yalnızca olarak işaretlenen yöntemler için geçerlidir static .Only applies to methods marked as static.
  • Yerel olmayan static işlevler içinde kullanılamaz & .Non-static local functions cannot be used in &. Bu yöntemlerin uygulama ayrıntıları, dil tarafından kasıtlı olarak belirtilmez.The implementation details of these methods are deliberately not specified by the language. Bu, statik ve örnek veya tam olarak hangi imzayı yayındıklarınızı içerir.This includes whether they are static vs. instance or exactly what signature they are emitted with.

Işlev Işaretçisi türlerindeki işleçlerOperators on Function Pointer Types

İşleçleri üzerinde güvenli olmayan kod bölümünde şu şekilde değişiklik yapılır:The section in unsafe code on operators is modified as such:

Güvenli olmayan bir bağlamda, _funcptr type_s olmayan tüm _pointer type_s üzerinde çalışan birkaç yapı mevcuttur _ _ :In an unsafe context, several constructs are available for operating on all _pointer_type_s that are not _funcptr_type_s:

Güvenli olmayan bir bağlamda, tüm _funcptr type_s için çeşitli yapılar kullanılabilir _ :In an unsafe context, several constructs are available for operating on all _funcptr_type_s:

Ayrıca, içindeki tüm bölümleri Pointers in expressions , ve dışında fordeklarasyon işlev işaretçisi türlerine değiştiririz Pointer comparison The sizeof operator .Additionally, we modify all the sections in Pointers in expressions to forbid function pointer types, except Pointer comparison and The sizeof operator.

Daha iyi işlev üyesiBetter function member

Daha iyi işlev üyesi belirtimi aşağıdaki satırı içerecek şekilde değiştirilecek:The better function member specification will be changed to include the following line:

delegate*, Şundan daha belirginvoid*A delegate* is more specific than void*

Diğer bir deyişle, ve ' de aşırı yükleme yapılabilir void* delegate* ve yine de sensibly adres işlecini kullanabilirsiniz.This means that it is possible to overload on void* and a delegate* and still sensibly use the address-of operator.

Tür ÇıkarmaType Inference

Güvenli olmayan kodda, tür çıkarımı algoritmalarında aşağıdaki değişiklikler yapılmıştır:In unsafe code, the following changes are made to the type inference algorithms:

Giriş türleriInput types

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#input-types

Aşağıdakiler eklenmiştir:The following is added:

Eğer bir E Yöntem grubudur ve T bir işlev işaretçisi türü ise, öğesinin tüm parametre türleri T türünde giriş türleridir E T .If E is an address-of method group and T is a function pointer type then all the parameter types of T are input types of E with type T.

Çıkış türleriOutput types

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#output-types

Aşağıdakiler eklenmiştir:The following is added:

Eğer bir E Yöntem grubu ise ve T bir işlev işaretçisi türü ise, öğesinin dönüş türü T türünde bir çıkış türüdür E T .If E is an address-of method group and T is a function pointer type then the return type of T is an output type of E with type T.

Çıkış türü ındaOutput type inferences

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#output-type-inferences

2 ve 3 ' ün arasına aşağıdaki madde işareti eklenir:The following bullet is added between bullets 2 and 3:

  • , E Bir adres yöntemi grubudur ve T parametre türleri ve dönüş türü olan bir işlev işaretçisi türüdür T1...Tk Tb ve türleri ile aşırı yükleme çözümlemesi, E T1..Tk dönüş türü ile tek bir yöntem alıyorsa U , ' den ' e daha düşük bağlantılı bir çıkarım yapılır U Tb .If E is an address-of method group and T is a function pointer type with parameter types T1...Tk and return type Tb, and overload resolution of E with the types T1..Tk yields a single method with return type U, then a lower-bound inference is made from U to Tb.

Tam ındaExact inferences

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#exact-inferences

Aşağıdaki alt madde işareti 2 ' ye işaret eden bir durum olarak eklenir:The following sub-bullet is added as a case to bullet 2:

  • V bir işlev işaretçisi türüdür delegate*<V2..Vk, V1> ve U bir işlev işaretçisi türüdür ve ' delegate*<U2..Uk, U1> ın çağırma kuralı ile aynıdır V U ve ' ın ' i ile Vi aynıdır Ui .V is a function pointer type delegate*<V2..Vk, V1> and U is a function pointer type delegate*<U2..Uk, U1>, and the calling convention of V is identical to U, and the refness of Vi is identical to Ui.

Alt sınırLower-bound inferences

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#lower-bound-inferences

Aşağıdaki durum 3 madde işaretine eklenmiştir:The following case is added to bullet 3:

  • V bir işlev işaretçisi türüdür delegate*<V2..Vk, V1> ve ile özdeş olan bir işlev işaretçisi türü vardır ve ' delegate*<U2..Uk, U1> U delegate*<U2..Uk, U1> ın çağırma kuralı V U Vi Ui ile aynıdır.V is a function pointer type delegate*<V2..Vk, V1> and there is a function pointer type delegate*<U2..Uk, U1> such that U is identical to delegate*<U2..Uk, U1>, and the calling convention of V is identical to U, and the refness of Vi is identical to Ui.

Öğesinden çıkarım ilk madde işareti Ui Vi Şu şekilde değiştirilir:The first bullet of inference from Ui to Vi is modified to:

  • UBir işlev işaretçisi türü değildir ve bir Ui başvuru türü olarak bilinmez ya da U bir işlev işaretçisi türü ise ve bir Ui işlev işaretçisi türü veya başvuru türü olarak bilinmiyorsa, tam bir çıkarım yapılırIf U is not a function pointer type and Ui is not known to be a reference type, or if U is a function pointer type and Ui is not known to be a function pointer type or a reference type, then an exact inference is made

Ardından, öğesinden çıkarım öğesinden sonra Ui Vi :Then, added after the 3rd bullet of inference from Ui to Vi:

  • Aksi takdirde, V delegate*<V2..Vk, V1> çıkarımı t-TH parametresine bağlıdır delegate*<V2..Vk, V1> :Otherwise, if V is delegate*<V2..Vk, V1> then inference depends on the i-th parameter of delegate*<V2..Vk, V1>:
    • V1:If V1:
      • Dönüş değere göre ise, alt sınır çıkarımı yapılır.If the return is by value, then a lower-bound inference is made.
      • Dönüş başvuruya göre ise, kesin bir çıkarım yapılır.If the return is by reference, then an exact inference is made.
    • V2... VKIf V2..Vk:
      • Parametre değere göre ise, üst sınır çıkarımı yapılır.If the parameter is by value, then an upper-bound inference is made.
      • Parametre başvuruya göre ise, kesin bir çıkarım yapılır.If the parameter is by reference, then an exact inference is made.

Üst sınırUpper-bound inferences

https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#upper-bound-inferences

Aşağıdaki örnek, 2. madde işaretine eklenmiştir:The following case is added to bullet 2:

  • U , bir işlev işaretçisi türüdür delegate*<U2..Uk, U1> ve ile V özdeş olan bir işlev işaretçisi türüdür ve ' nin, ile aynı, delegate*<V2..Vk, V1> U V ' Ui Vi nin de aynısı ile aynıdır.U is a function pointer type delegate*<U2..Uk, U1> and V is a function pointer type which is identical to delegate*<V2..Vk, V1>, and the calling convention of U is identical to V, and the refness of Ui is identical to Vi.

Öğesinden çıkarım ilk madde işareti Ui Vi Şu şekilde değiştirilir:The first bullet of inference from Ui to Vi is modified to:

  • UBir işlev işaretçisi türü değildir ve bir Ui başvuru türü olarak bilinmez ya da U bir işlev işaretçisi türü ise ve bir Ui işlev işaretçisi türü veya başvuru türü olarak bilinmiyorsa, tam bir çıkarım yapılırIf U is not a function pointer type and Ui is not known to be a reference type, or if U is a function pointer type and Ui is not known to be a function pointer type or a reference type, then an exact inference is made

Ardından, öğesinden çıkarım öğesinden sonra Ui Vi :Then added after the 3rd bullet of inference from Ui to Vi:

  • Aksi takdirde, U delegate*<U2..Uk, U1> çıkarımı t-TH parametresine bağlıdır delegate*<U2..Uk, U1> :Otherwise, if U is delegate*<U2..Uk, U1> then inference depends on the i-th parameter of delegate*<U2..Uk, U1>:
    • Eğer U1:If U1:
      • Dönüş değere göre ise, üst sınır çıkarımı yapılır.If the return is by value, then an upper-bound inference is made.
      • Dönüş başvuruya göre ise, kesin bir çıkarım yapılır.If the return is by reference, then an exact inference is made.
    • Eğer U2... TrIf U2..Uk:
      • Parametre değere göre ise, alt sınır çıkarımı yapılır.If the parameter is by value, then a lower-bound inference is made.
      • Parametre başvuruya göre ise, kesin bir çıkarım yapılır.If the parameter is by reference, then an exact inference is made.

in, out , Ve ref readonly parametrelerinin ve dönüş türlerinin meta veri temsiliMetadata representation of in, out, and ref readonly parameters and return types

İşlev işaretçisi imzalarının parametre bayrakları konumu yok, bu nedenle parametre ve dönüş türünün in ,, out veya ref readonly modreqs kullanarak kodlanması gerekir.Function pointer signatures have no parameter flags location, so we must encode whether parameters and the return type are in, out, or ref readonly by using modreqs.

in

System.Runtime.InteropServices.InAttributeBir modreq parametre ya da dönüş türü üzerinde başvuru belirticisine bir olarak, aşağıdaki gibi bir değer olarak uygulandıktan sonra yeniden kullanıyoruz:We reuse System.Runtime.InteropServices.InAttribute, applied as a modreq to the ref specifier on a parameter or return type, to mean the following:

  • Bir parametre başvurusu belirticisine uygulanmışsa, bu parametre olarak değerlendirilir in .If applied to a parameter ref specifier, this parameter is treated as in.
  • Dönüş türü başvuru belirticisine uygulanmışsa, dönüş türü olarak değerlendirilir ref readonly .If applied to the return type ref specifier, the return type is treated as ref readonly.

out

Parametresi bir parametre System.Runtime.InteropServices.OutAttribute modreq olduğu anlamına gelen bir parametre türü üzerinde başvuru belirleyicisi olarak uygulandı out .We use System.Runtime.InteropServices.OutAttribute, applied as a modreq to the ref specifier on a parameter type, to mean that the parameter is an out parameter.

HatalarErrors

  • OutAttributeBir dönüş türüne modreq olarak uygulanması hatadır.It is an error to apply OutAttribute as a modreq to a return type.
  • Hem hem de bir InAttribute OutAttribute parametre türüne modreq olarak uygulamak hatadır.It is an error to apply both InAttribute and OutAttribute as a modreq to a parameter type.
  • Her biri modopt ile belirtilirse, bunlar yoksayılır.If either are specified via modopt, they are ignored.

Çağırma kurallarının meta veri temsiliMetadata Representation of Calling Conventions

Çağırma kuralları, İmzadaki bayrağın bir birleşimi ile meta verilerde bir yöntem imzasında kodlanır CallKind ve imzanın başlangıcında sıfır veya daha fazla modopt s.Calling conventions are encoded in a method signature in metadata by a combination of the CallKind flag in the signature and zero or more modopts at the start of the signature. ECMA-335 Şu anda bayrakta aşağıdaki öğeleri bildiriyor CallKind :ECMA-335 currently declares the following elements in the CallKind flag:

CallKind
   : default
   | unmanaged cdecl
   | unmanaged fastcall
   | unmanaged thiscall
   | unmanaged stdcall
   | varargs
   ;

Bunlar, C# ' deki işlev işaretçileri, ancak tümünü destekleyecektir varargs .Of these, function pointers in C# will support all but varargs.

Ayrıca, çalışma zamanı (ve sonuç olarak 335) yeni platformları kapsayacak şekilde güncelleştirilecektir CallKind .In addition, the runtime (and eventually 335) will be updated to include a new CallKind on new platforms. Bu, şu anda resmi bir ada sahip değil, ancak bu belge unmanaged ext yeni Genişletilebilir çağırma kuralı biçimi için tek bir yer tutucu olarak kullanılacak.This does not have a formal name currently, but this document will use unmanaged ext as a placeholder to stand for the new extensible calling convention format. Her ikisi de, modopt unmanaged ext unmanaged köşeli parantezler olmadan platform varsayılan çağırma kuralıdır.With no modopts, unmanaged ext is the platform default calling convention, unmanaged without the square brackets.

İle arasında eşleme calling_convention_specifier``CallKindMapping the calling_convention_specifier to a CallKind

calling_convention_specifierAtlanan veya olarak belirtilen bir, managed ile eşlenir default CallKind .A calling_convention_specifier that is omitted, or specified as managed, maps to the default CallKind. Bu, öğesine CallKind sahip olmayan her yöntemin varsayılandır UnmanagedCallersOnly .This is default CallKind of any method not attributed with UnmanagedCallersOnly.

C#, ECMA 335 ' den mevcut olan yönetilmeyen s 'lerden eşlenen 4 özel tanımlayıcıyı tanır CallKind .C# recognizes 4 special identifiers that map to specific existing unmanaged CallKinds from ECMA 335. Bu eşlemenin gerçekleşmesi için, bu tanımlayıcıların başka tanımlayıcı olmadan, kendi üzerinde belirtilmesi gerekir ve bu gereksinim, s için spec olarak kodlanır unmanaged_calling_convention .In order for this mapping to occur, these identifiers must be specified on their own, with no other identifiers, and this requirement is encoded into the spec for unmanaged_calling_conventions. Bu tanımlayıcılar,,, ve sırasıyla,,, Cdecl Thiscall Stdcall Fastcall unmanaged cdecl unmanaged thiscall unmanaged stdcall ve ' a karşılık gelen unmanaged fastcall ,, ve.These identifiers are Cdecl, Thiscall, Stdcall, and Fastcall, which correspond to unmanaged cdecl, unmanaged thiscall, unmanaged stdcall, and unmanaged fastcall, respectively. Birden fazla identifer belirtilirse veya tek identifier özel tanınan tanımlayıcılardan değilse, tanımlayıcıda aşağıdaki kurallara sahip özel ad araması yaptık:If more than one identifer is specified, or the single identifier is not of the specially recognized identifiers, we perform special name lookup on the identifier with the following rules:

  • Dizeden sonuna kadar identifier``CallConvWe prepend the identifier with the string CallConv
  • Yalnızca ad alanında tanımlanan türlere bakacağız System.Runtime.CompilerServices .We look only at types defined in the System.Runtime.CompilerServices namespace.
  • Yalnızca uygulamanın çekirdek kitaplığında tanımlı türlere göz atacağız. Bu kitaplık, tanımlayan ve bağımlılığı olmayan bir kitaplıktır System.Object .We look only at types defined in the core library of the application, which is the library that defines System.Object and has no dependencies.
  • Yalnızca ortak türlere baktık.We look only at public types.

Arama, identifier bir içinde belirtilen tüm öğeleri üzerinde başarılı olursa, unmanaged_calling_convention as olarak kodlarız CallKind unmanaged ext ve modopt işlev işaretçisi imzasının başlangıcında bulunan her bir çözümlenmiş türü kodlayın.If lookup succeeds on all of the identifiers specified in an unmanaged_calling_convention, we encode the CallKind as unmanaged ext, and encode each of the resolved types in the set of modopts at the beginning of the function pointer signature. Bir notta, bu kurallar identifier , kullanıcıların aramaya yol açacak şekilde bu öğeleri uygulamasına ön eki gerçekleştiremediğini ifade CallConv eder CallConvCallConvVectorCall .As a note, these rules mean that users cannot prefix these identifiers with CallConv, as that will result in looking up CallConvCallConvVectorCall.

Meta verileri yorumlarken, ilk olarak konusuna baktık CallKind .When interpreting metadata, we first look at the CallKind. ' Den başka bir şey ise unmanaged ext , modopt çağırma kuralını belirleme ve yalnızca öğesini kullanma amacıyla, dönüş türündeki tüm öğeleri yok sayıyoruz CallKind .If it is anything other than unmanaged ext, we ignore all modopts on the return type for the purposes of determining the calling convention, and use only the CallKind. İse, CallKind unmanaged ext işlev işaretçisi türünün başlangıcında, aşağıdaki gereksinimleri karşılayan tüm türlerin birleşimini alarak modopts ' a bakacağız:If the CallKind is unmanaged ext, we look at the modopts at the start of the function pointer type, taking the union of all types that meet the following requirements:

  • , Başka bir kitaplıklara ve tanımlanmadığında, kitaplık olan Çekirdek Kitaplığı 'nda tanımlanmıştır System.Object .The is defined in the core library, which is the library that references no other libraries and defines System.Object.
  • Tür System.Runtime.CompilerServices ad alanında tanımlanır.The type is defined in the System.Runtime.CompilerServices namespace.
  • Tür önekiyle başlar CallConv .The type starts with the prefix CallConv.
  • Tür geneldir.The type is public.

Bunlar, identifier unmanaged_calling_convention kaynakta bir işlev işaretçisi türü tanımlarken bir üzerinde arama gerçekleştirirken bulunması gereken türleri temsil eder.These represent the types that must be found when performing lookup on the identifiers in an unmanaged_calling_convention when defining a function pointer type in source.

CallKind unmanaged ext Hedef çalışma zamanı özelliği desteklemiyorsa, ile bir işlev işaretçisi kullanma girişimi bir hatadır.It is an error to attempt to use a function pointer with a CallKind of unmanaged ext if the target runtime does not support the feature. Bu işlem, sabit varlığına bakılarak belirlenir System.Runtime.CompilerServices.RuntimeFeature.UnmanagedCallKind .This will be determined by looking for the presence of the System.Runtime.CompilerServices.RuntimeFeature.UnmanagedCallKind constant. Bu sabit varsa, çalışma zamanı özelliği desteklemek için kabul edilir.If this constant is present, the runtime is considered to support the feature.

System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute

System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute , bir yöntemin belirli bir çağırma kuralıyla çağrılması gerektiğini göstermek için CLR tarafından kullanılan bir özniteliktir.System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute is an attribute used by the CLR to indicate that a method should be called with a specific calling convention. Bu nedenle, özniteliğiyle çalışma için aşağıdaki desteği sunuyoruz:Because of this, we introduce the following support for working with the attribute:

  • C# ' den bu öznitelikle açıklanmış bir yöntemi doğrudan çağırmak hatadır.It is an error to directly call a method annotated with this attribute from C#. Kullanıcılar yöntemine bir işlev işaretçisi almalıdır ve sonra bu işaretçiyi çağırmalıdır.Users must obtain a function pointer to the method and then invoke that pointer.
  • Özniteliği sıradan statik bir yöntem veya sıradan statik yerel işlev dışında bir şeye uygulamak hatadır.It is an error to apply the attribute to anything other than an ordinary static method or ordinary static local function. C# derleyicisi, Bu öznitelikle meta verilerden içeri aktarılan statik olmayan veya statik olmayan, normal olmayan yöntemlerin dilini tarafından desteklenmeyen şekilde işaretleyecek.The C# compiler will mark any non-static or static non-ordinary methods imported from metadata with this attribute as unsupported by the language.
  • Özniteliği ile işaretlenmiş ve bir parametre ya da dönüş türü olmayan bir yöntem için bir hatadır unmanaged_type .It is an error for a method marked with the attribute to have a parameter or return type that is not an unmanaged_type.
  • Bu tür parametreleri ile kısıtlansa bile, öznitelik ile işaretlenmiş bir yöntemin tür parametrelerine sahip olması hatadır unmanaged .It is an error for a method marked with the attribute to have type parameters, even if those type parameters are constrained to unmanaged.
  • Bu, genel türdeki bir yöntemin özniteliğiyle işaretlenmesi hatadır.It is an error for a method in a generic type to be marked with the attribute.
  • Özniteliği ile işaretlenmiş bir yöntemi temsilci türüne dönüştürmek hatadır.It is an error to convert a method marked with the attribute to a delegate type.
  • UnmanagedCallersOnly.CallConvsMeta verilerde, kuralları çağırma gereksinimlerini karşılamayan herhangi bir tür belirtmek bir hatadır modopt .It is an error to specify any types for UnmanagedCallersOnly.CallConvs that do not meet the requirements for calling convention modopts in metadata.

Geçerli bir öznitelik ile işaretlenmiş bir yöntemin çağırma kuralını belirlerken UnmanagedCallersOnly , derleyici, CallConvs CallKind modopt çağırma kuralını belirlemek için kullanılması gereken geçerli ve s 'yi belirlemek için özelliğinde belirtilen türler üzerinde aşağıdaki denetimleri gerçekleştirir:When determining the calling convention of a method marked with a valid UnmanagedCallersOnly attribute, the compiler performs the following checks on the types specified in the CallConvs property to determine the effective CallKind and modopts that should be used to determine the calling convention:

  • Hiçbir tür belirtilmemişse, CallKind unmanaged ext modopt işlev işaretçisi türünün başlangıcında hiçbir çağırma kuralı olmadan olarak değerlendirilir.If no types are specified, the CallKind is treated as unmanaged ext, with no calling convention modopts at the start of the function pointer type.
  • Bir tür belirtilmişse ve bu tür,, veya olarak adlandırılmışsa CallConvCdecl , CallConvThiscall CallConvStdcall CallConvFastcall CallKind unmanaged cdecl unmanaged thiscall unmanaged stdcall unmanaged fastcall modopt işlev işaretçisi türünün başlangıcında hiçbir çağırma kuralı olmadan,,, veya olarak değerlendirilir.If there is one type specified, and that type is named CallConvCdecl, CallConvThiscall, CallConvStdcall, or CallConvFastcall, the CallKind is treated as unmanaged cdecl, unmanaged thiscall, unmanaged stdcall, or unmanaged fastcall, respectively, with no calling convention modopts at the start of the function pointer type.
  • Birden çok tür belirtilmişse veya tek tür Yukarıdaki özel olarak çağrılan çıkış türlerinden biri olarak adlandırılmışsa, CallKind unmanaged ext modopt işlev işaretçisi türünün başlangıcında, belirtilen türlerin birleşimi ile olarak değerlendirilir.If multiple types are specified or the single type is not named one of the specially called out types above, the CallKind is treated as unmanaged ext, with the union of the types specified treated as modopts at the start of the function pointer type.

Daha sonra derleyici bu etkin CallKind ve koleksiyona bakar modopt ve işlev işaretçisi türünün son çağırma kuralını belirlemede normal meta veri kurallarını kullanır.The compiler then looks at this effective CallKind and modopt collection and uses normal metadata rules to determine the final calling convention of the function pointer type.

Açık Uçlu SorularOpen Questions

İçin çalışma zamanı desteği algılanıyor unmanaged extDetecting runtime support for unmanaged ext

https://github.com/dotnet/runtime/issues/38135 Bu bayrağı eklemeyi izler.https://github.com/dotnet/runtime/issues/38135 tracks adding this flag. Gözden geçirimden geri bildirime bağlı olarak, sorun içinde belirtilen özelliğini kullanacağız veya çalışma UnmanagedCallersOnlyAttribute zamanlarının destekleyip desteklemediğini belirleyen bayrak olarak varlığını kullanın unmanaged ext .Depending on the feedback from review, we will either use the property specified in the issue, or use the presence of UnmanagedCallersOnlyAttribute as the flag that determines whether the runtimes supports unmanaged ext.

Dikkat edilmesi gerekenlerConsiderations

Örnek yöntemlerine izin verAllow instance methods

Teklif, EXPLICITTHIS CLI çağırma kuralı (C# kodunda adlandırılmış olarak adlandırılır) avantajlarından yararlanarak örnek yöntemlerini desteklemek için Genişletilebilir instance .The proposal could be extended to support instance methods by taking advantage of the EXPLICITTHIS CLI calling convention (named instance in C# code). CLı işlev işaretçilerinden oluşan bu form, this parametreyi işlev işaretçisi sözdiziminin açık bir ilk parametresi olarak koyar.This form of CLI function pointers puts the this parameter as an explicit first parameter of the function pointer syntax.

unsafe class Instance {
    void Use() {
        delegate* instance<Instance, string> f = &ToString;
        f(this);
    }
}

Bu ses, ancak teklife bazı karmaşıkma ekler.This is sound but adds some complication to the proposal. Özellikle, çağırma kuralı tarafından farklı olan instance ve managed her iki durum da aynı C# imzasıyla yönetilen yöntemleri çağırmak için kullanılan işlev işaretçileri nedeniyle uyumsuz olacak şekilde,Particularly because function pointers which differed by the calling convention instance and managed would be incompatible even though both cases are used to invoke managed methods with the same C# signature. Her durumda, bu durumun basit bir iş olması için değerli olduğu kabul edilir: static yerel bir işlev kullanın.Also in every case considered where this would be valuable to have there was a simple work around: use a static local function.

unsafe class Instance {
    void Use() {
        static string toString(Instance i) => i.ToString();
        delegate*<Instance, string> f = &toString;
        f(this);
    }
}

Bildirimde güvenli olmayan isteDon't require unsafe at declaration

unsafeHer bir kullanımını gerektirmek yerine delegate* , yalnızca bir yöntem grubunun öğesine dönüştürüldüğü noktada bunu gerektir delegate* .Instead of requiring unsafe at every use of a delegate*, only require it at the point where a method group is converted to a delegate*. Bu, temel güvenlik sorunlarının oynatılmasına (değer etkin olduğu sürece kapsayan derlemenin kaldırılamayacağını bilmek) neden olur.This is where the core safety issues come into play (knowing that the containing assembly cannot be unloaded while the value is alive). unsafeDiğer konumlar için ihtiyaç duyulmaları çok fazla olabilir.Requiring unsafe on the other locations can be seen as excessive.

Tasarımın ilk amacı budur.This is how the design was originally intended. Ancak elde edilen dil kuralları, çok daha fazla.But the resulting language rules felt very awkward. Bu bir işaretçi değeri olduğu ve anahtar sözcüğü olmadan bile üzerinde gezindiğinden emin olmak mümkün değildir unsafe .It's impossible to hide the fact that this is a pointer value and it kept peeking through even without the unsafe keyword. Örneğin dönüştürmeye object izin verilmiyorsa, class , vb. bir üyesi olamaz. C# tasarımı unsafe Tüm işaretçinin kullanması için ve bu tasarımın bundan sonra bunu takip etmek için gereklidir.For example the conversion to object can't be allowed, it can't be a member of a class, etc ... The C# design is to require unsafe for all pointer uses and hence this design follows that.

Geliştiriciler yine de delegate* normal işaretçi türlerinde gerçekleştirdikleri şekilde, değerlerin üzerine güvenli bir sarmalayıcı sunma yeteneğine sahip olmaya devam edecektir.Developers will still be capable of presenting a safe wrapper on top of delegate* values the same way that they do for normal pointer types today. Aşağıdakileri dikkate alın:Consider:

unsafe struct Action {
    delegate*<void> _ptr;

    Action(delegate*<void> ptr) => _ptr = ptr;
    public void Invoke() => _ptr();
}

Temsilcileri kullanmaUsing delegates

Yeni bir sözdizimi öğesi kullanmak yerine, delegate* mevcut delegate türleri aşağıdaki türle birlikte kullanmanız yeterlidir * :Instead of using a new syntax element, delegate*, simply use existing delegate types with a * following the type:

Func<object, object, bool>* ptr = &object.ReferenceEquals;

Çağırma kuralını işleme, delegate bir değeri belirten bir özniteliğe sahip türlere açıklama eklenerek yapılabilir CallingConvention .Handling calling convention can be done by annotating the delegate types with an attribute that specifies a CallingConvention value. Bir özniteliğin olmaması yönetilen çağırma kuralını işaret eder.The lack of an attribute would signify the managed calling convention.

Il 'de bunu kodlamada sorun vardır.Encoding this in IL is problematic. Temel alınan değerin bir işaretçi olarak temsil edilebilmesi için yine de şunları yapmanız gerekir:The underlying value needs to be represented as a pointer yet it also must:

  1. Farklı işlev işaretçisi türleri olan aşırı yüklemelerin izin vermek için benzersiz bir türü vardır.Have a unique type to allow for overloads with different function pointer types.
  2. Derleme sınırları genelinde OHI amaçları için eşdeğer olmalıdır.Be equivalent for OHI purposes across assembly boundaries.

Son nokta özellikle sorunlu.The last point is particularly problematic. Bu, kullanan her derlemenin, Func<int>* Func<int>* bir derlemede tanımlansa bile, meta verilerde eşdeğer bir tür kodlanması gereken anlamına gelir.This mean that every assembly which uses Func<int>* must encode an equivalent type in metadata even though Func<int>* is defined in an assembly though don't control. Ayrıca System.Func<T> , mscorlib olmayan bir derlemede tanımlanan diğer herhangi bir tür, mscorlib 'de tanımlanan sürümden farklı olmalıdır.Additionally any other type which is defined with the name System.Func<T> in an assembly that is not mscorlib must be different than the version defined in mscorlib.

Araştırılan bir seçenek bu tür bir işaretçiyi gibi yaymıştı mod_req(Func<int>) void* .One option that was explored was emitting such a pointer as mod_req(Func<int>) void*. Bir mod_req öğesine bağlanamaz TypeSpec ve bu nedenle genel örneklemeleri hedeflenemez.This doesn't work though as a mod_req cannot bind to a TypeSpec and hence cannot target generic instantiations.

Adlandırılmış işlev işaretçileriNamed function pointers

İşlev işaretçisi sözdizimi, özellikle de iç içe geçmiş işlev işaretçileri gibi karmaşık durumlarda, kısabera olabilir.The function pointer syntax can be cumbersome, particularly in complex cases like nested function pointers. Geliştiricilerin, ile yapıldığı gibi işlev işaretçilerinin adlandırılmış bildirimleri için izin verebilen her seferinde imzayı yazmak yerine delegateRather than have developers type out the signature every time the language could allow for named declarations of function pointers as is done with delegate.

func* void Action();

unsafe class NamedExample {
    void M(Action a) {
        a();
    }
}

Sorunun bir bölümü, temel alınan CLı ilkel adının adı yoktur, bu nedenle yalnızca bir C# listesi olur ve bir bit meta veri işinin etkinleştirilmesini gerektirir.Part of the problem here is the underlying CLI primitive doesn't have names hence this would be purely a C# invention and require a bit of metadata work to enable. Bu, iş açısından önemli olan, ancak önemli bir çalışmadır.That is doable but is a significant about of work. Aslında C# ' ın yalnızca bu adlar için def tablosuna yönelik bir yardımcısı olmasını gerektirir.It essentially requires C# to have a companion to the type def table purely for these names.

Ayrıca, adlandırılmış işlev işaretçileri için bağımsız değişkenler incelendikleri zaman, diğer birçok senaryoya eşit bir şekilde uygulanamazlar.Also when the arguments for named function pointers were examined we found they could apply equally well to a number of other scenarios. Örneğin, tüm durumlarda tam imzayı yazma ihtiyacını azaltmak için adlandırılmış tanımlama gruplarını bildirmek uygun olabilir.For example it would be just as convenient to declare named tuples to reduce the need to type out the full signature in all cases.

(int x, int y) Point;

class NamedTupleExample {
    void M(Point p) {
        Console.WriteLine(p.x);
    }
}

Tartışmadan sonra, türlerin adlandırılmış bildirimine izin vermeyi karardık delegate* .After discussion we decided to not allow named declaration of delegate* types. Bu, müşteri kullanımı geri bildirimlerine göre bu için önemli bir gereksinim olduğunu fark etmemiz halinde işlev işaretçileri, tanımlama grupları, genel türler, vb. için uygun bir adlandırma çözümü araştıracağız. Bu, formda tam destek gibi diğer önerilere benzer olabilir typedef .If we find there is significant need for this based on customer usage feedback then we will investigate a naming solution that works for function pointers, tuples, generics, etc ... This is likely to be similar in form to other suggestions like full typedef support in the language.

Gelecekteki konularFuture Considerations

statik temsilcilerstatic delegates

Bu, delegate yalnızca üyelere başvurabilen türlerin bildirimine izin veren öneriyi ifade eder static .This refers to the proposal to allow for the declaration of delegate types which can only refer to static members. Bu tür örneklerin avantajı, delegate performans duyarlı senaryolarda ücretsiz ve daha iyi bir şekilde tahsis edilebilir.The advantage being that such delegate instances can be allocation free and better in performance sensitive scenarios.

İşlev işaretçisi özelliği uygulanmışsa, static delegate teklif büyük olasılıkla kapatılmış olur. Bu özelliğin önerilen avantajı, ayırma ücretsiz doğası olur.If the function pointer feature is implemented the static delegate proposal will likely be closed out. The proposed advantage of that feature is the allocation free nature. Ancak son araştırmalar, derleme kaldırma işlemi nedeniyle elde edilebilecek mümkün olmayan bir şekilde bulundu.However recent investigations have found that is not possible to achieve due to assembly unloading. static delegateDerlemenin içinden bellekten kaldırılmasını önlemek için, öğesinden başvurduğu yönteme güçlü bir tanıtıcı olmalıdır.There must be a strong handle from the static delegate to the method it refers to in order to keep the assembly from being unloaded out from under it.

Her örneği sürdürmek için, static delegate teklifin hedeflerine sayaç çalıştıran yeni bir tanıtıcı ayrılması gerekir.To maintain every static delegate instance would be required to allocate a new handle which runs counter to the goals of the proposal. Ayırmanın çağrı-site başına tek bir ayırmaya salsatılabileceği, ancak bir bit karmaşıktı olduğu ve bu da ticareti kapatmasının gerektiği bazı tasarımlar vardı.There were some designs where the allocation could be amortized to a single allocation per call-site but that was a bit complex and didn't seem worth the trade off.

Diğer bir deyişle, geliştiriciler temelde aşağıdaki denge ile karar vermek zorunda değildir:That means developers essentially have to decide between the following trade offs:

  1. Derlemeyi kaldırma aşamasında güvenlik: Bu ayırmaları gerektirir ve bu nedenle delegate zaten yeterli bir seçenektir.Safety in the face of assembly unloading: this requires allocations and hence delegate is already a sufficient option.
  2. Derleme kaldırma aşamasında güvenlik yok: bir kullanın delegate* .No safety in face of assembly unloading: use a delegate*. Bu, struct kodun geri kalanında bir bağlam dışında kullanılmasına izin vermek için bir öğesine kaydırılmış olabilir unsafe .This can be wrapped in a struct to allow usage outside an unsafe context in the rest of the code.