Function pointers - C# 9.0 specification proposals

文章推薦指數: 80 %
投票人數:10人

//This method has a managed calling convention. ... delegate Func1 Func2(Func1 f); // Function pointer equivalent without calling convention ... 跳到主要內容 已不再支援此瀏覽器。

請升級至MicrosoftEdge,以利用最新功能、安全性更新和技術支援。

下載MicrosoftEdge 其他資訊 目錄 結束焦點模式 閱讀英文 儲存 目錄 閱讀英文 儲存 編輯 Twitter LinkedIn Facebook 電子郵件 目錄 函式指標FunctionPointers 發行項 05/13/2021 2位參與者 本文內容 總結Summary 這份提案提供的語言結構,會公開目前無法有效存取的ILopcode,或目前在c#中的程式:ldftn和calli。

ThisproposalprovideslanguageconstructsthatexposeILopcodesthatcannotcurrentlybeaccessedefficiently,oratall,inC#today:ldftnandcalli.這些ILopcode在高效能的程式碼中可能很重要,而開發人員需要有效率的方式來存取它們。

TheseILopcodescanbeimportantinhighperformancecodeanddevelopersneedanefficientwaytoaccessthem. 動機Motivation 下列問題將說明這項功能的動機和背景,(為功能)的可能執行:Themotivationsandbackgroundforthisfeaturearedescribedinthefollowingissue(asisapotentialimplementationofthefeature): https://github.com/dotnet/csharplang/issues/191 這是編譯器內建函式的替代設計提案Thisisanalternatedesignproposaltocompilerintrinsics 詳細設計DetailedDesign 函式指標Functionpointers 語言將允許使用語法來宣告函式指標delegate*。

Thelanguagewillallowforthedeclarationoffunctionpointersusingthedelegate*syntax.下一節會詳細描述完整的語法,但是它的目的是與和型別宣告所使用的語法類似FuncAction。

ThefullsyntaxisdescribedindetailinthenextsectionbutitismeanttoresemblethesyntaxusedbyFuncandActiontypedeclarations. unsafeclassExample{ voidExample(Actiona,delegate*f){ a(42); f(42); } } 這些類型會使用如ECMA-335中所述的函式指標類型來表示。

ThesetypesarerepresentedusingthefunctionpointertypeasoutlinedinECMA-335.這表示叫用將delegate*會使用calli,其中的調用delegate會callvirt在方法上使用Invoke。

Thismeansinvocationofadelegate*willusecalliwhereinvocationofadelegatewillusecallvirtontheInvokemethod. 在語法上,這兩個結構的調用完全相同。

Syntacticallythoughinvocationisidenticalforbothconstructs. 方法指標的ECMA-335定義包含做為類型簽章一部分的呼叫慣例,(區段7.1)。

TheECMA-335definitionofmethodpointersincludesthecallingconventionaspartofthetypesignature(section7.1). 預設呼叫慣例將為managed。

Thedefaultcallingconventionwillbemanaged.未受管理的呼叫慣例可以藉由unmanaged將關鍵字放afer語法來指定delegate*,這會使用執行時間平臺的預設值。

Unmanagedcallingconventionscanbyspecifiedbyputtinganunmanagedkeywordaferthedelegate*syntax,whichwillusetheruntimeplatformdefault.然後,您可以在命名空間中指定開頭為的任何類型,以括弧括住特定的非受控慣例unmanagedCallConvSystem.Runtime.CompilerServices,並保留CallConv前置詞。

SpecificunmanagedconventionscanthenbespecifiedinbracketstotheunmanagedkeywordbyspecifyinganytypestartingwithCallConvintheSystem.Runtime.CompilerServicesnamespace,leavingofftheCallConvprefix.這些類型必須來自于程式的核心程式庫,而有效組合的集合則與平臺相關。

Thesetypesmustcomefromtheprogram'scorelibrary,andthesetofvalidcombinationsisplatform-dependent. //Thismethodhasamanagedcallingconvention.Thisisthesameasleavingthemanagedkeywordoff. delegate*managed; //Thismethodwillbeinvokedusingwhateverthedefaultunmanagedcallingconventionontheruntime //platformis.ThisisplatformandarchitecturedependentandisdeterminedbytheCLRatruntime. delegate*unmanaged; //Thismethodwillbeinvokedusingthecdeclcallingconvention //CdeclmapstoSystem.Runtime.CompilerServices.CallConvCdecl delegate*unmanaged[Cdecl]; //Thismethodwillbeinvokedusingthestdcallcallingconvention,andsuppressesGCtransition //StdcallmapstoSystem.Runtime.CompilerServices.CallConvStdcall //SuppressGCTransitionmapstoSystem.Runtime.CompilerServices.CallConvSuppressGCTransition delegate*unmanaged[Stdcall,SuppressGCTransition]; 類型之間的轉換delegate*是根據其簽章(包括呼叫慣例)來完成。

Conversionsbetweendelegate*typesisdonebasedontheirsignatureincludingthecallingconvention. unsafeclassExample{ voidConversions(){ delegate*p1=...; delegate*managedp2=...; delegate*unmanagedp3=...; p1=p2;//okayp1andp2havecompatiblesignatures Console.WriteLine(p2==p1);//True p2=p3;//error:callingconventionsareincompatible } } delegate*類型是指標類型,這表示它具有標準指標類型的所有功能和限制:Adelegate*typeisapointertypewhichmeansithasallofthecapabilitiesandrestrictionsofastandardpointertype: 僅在內容中有效unsafe。

Onlyvalidinanunsafecontext. delegate*只能從內容呼叫包含參數或傳回類型的方法unsafe。

Methodswhichcontainadelegate*parameterorreturntypecanonlybecalledfromanunsafecontext. 無法轉換成object。

Cannotbeconvertedtoobject. 無法當做泛型引數使用。

Cannotbeusedasagenericargument. 可以隱含地轉換delegate*成void*。

Canimplicitlyconvertdelegate*tovoid*. 可以明確地從轉換成void*delegate*。

Canexplicitlyconvertfromvoid*todelegate*. 限制:Restrictions: 自訂屬性無法套用至delegate*或其任何元素。

Customattributescannotbeappliedtoadelegate*oranyofitselements. delegate*參數不可標記為paramsAdelegate*parametercannotbemarkedasparams delegate*類型具有一般指標類型的所有限制。

Adelegate*typehasalloftherestrictionsofanormalpointertype. 指標算術無法直接在函式指標類型上執行。

Pointerarithmeticcannotbeperformeddirectlyonfunctionpointertypes. 函數指標語法Functionpointersyntax 完整函式指標語法是以下列文法表示:Thefullfunctionpointersyntaxisrepresentedbythefollowinggrammar: pointer_type :... |funcptr_type ; funcptr_type :'delegate''*'calling_convention_specifier?'' ; 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' |'refreadonly' ; 如果未calling_convention_specifier提供,則預設值為managed。

Ifnocalling_convention_specifierisprovided,thedefaultismanaged.的精確中繼資料編碼calling_convention_specifier和identifier中有效的,unmanaged_calling_convention會包含在呼叫慣例的中繼資料標記法中。

Theprecisemetadataencodingofthecalling_convention_specifierandwhatidentifiersarevalidintheunmanaged_calling_conventioniscoveredinMetadataRepresentationofCallingConventions. delegateintFunc1(strings); delegateFunc1Func2(Func1f); //Functionpointerequivalentwithoutcallingconvention delegate*; delegate*,delegate*>; //Functionpointerequivalentwithcallingconvention delegate*managed; delegate*,delegate*>; 函式指標轉換Functionpointerconversions 在unsafe內容中,可用的隱含轉換集(隱含轉換)擴充成包含下列隱含指標轉換:Inanunsafecontext,thesetofavailableimplicitconversions(Implicitconversions)isextendedtoincludethefollowingimplicitpointerconversions: 現有的轉換Existingconversions 從funcptr_類型F0到另一種funcptr_類型F1,前提是下列各項都成立:Fromfuncptr_typeF0toanotherfuncptr_typeF1,providedallofthefollowingaretrue: F0和F1擁有相同數目的參數,且中的每個參數與D0nF0refoutin中的對應參數具有相同的、或D1nF1修飾詞。

F0andF1havethesamenumberofparameters,andeachparameterD0ninF0hasthesameref,out,orinmodifiersasthecorrespondingparameterD1ninF1. 針對每個值參數(沒有ref、或修飾詞的參數outin)、識別轉換、隱含參考轉換或隱含指標轉換,都是從的參數類型F0到中的對應參數類型F1。

Foreachvalueparameter(aparameterwithnoref,out,orinmodifier),anidentityconversion,implicitreferenceconversion,orimplicitpointerconversionexistsfromtheparametertypeinF0tothecorrespondingparametertypeinF1. 針對每個ref、out或in參數,中的參數類型與F0中的對應參數型別相同F1。

Foreachref,out,orinparameter,theparametertypeinF0isthesameasthecorrespondingparametertypeinF1. 如果傳回型別是以value(no或),則會將傳回型別中的傳回型別refrefreadonly(identity、隱含參考或隱含指標轉換)存在於的傳回型別F1F0。

Ifthereturntypeisbyvalue(noreforrefreadonly),anidentity,implicitreference,orimplicitpointerconversionexistsfromthereturntypeofF1tothereturntypeofF0. 如果傳回型別是傳址(或),的傳回型別和修飾詞就會與的傳回型別和修飾詞refrefreadonlyrefF1相同refF0。

Ifthereturntypeisbyreference(reforrefreadonly),thereturntypeandrefmodifiersofF1arethesameasthereturntypeandrefmodifiersofF0. 的呼叫慣例與F0的呼叫慣例相同F1。

ThecallingconventionofF0isthesameasthecallingconventionofF1. 允許目標方法的位址Allowaddress-oftotargetmethods 現在可將方法群組作為位址運算式的引數。

Methodgroupswillnowbeallowedasargumentstoanaddress-ofexpression.這類運算式的類型會是具有對delegate*等的目標方法簽章和managed呼叫慣例的:Thetypeofsuchanexpressionwillbeadelegate*whichhastheequivalentsignatureofthetargetmethodandamanagedcallingconvention: unsafeclassUtil{ publicstaticvoidLog(){} voidUse(){ delegate*ptr1=&Util.Log; //Error:type"delegate*"notcompatiblewith"delegate*"; delegate*ptr2=&Util.Log; } } 在unsafe內容中,MF如果下列所有條件都成立,則方法與函式指標類型相容:Inanunsafecontext,amethodMiscompatiblewithafunctionpointertypeFifallofthefollowingaretrue: M和F擁有相同數目的參數,且中的每個參數與Mrefoutin中的對應參數具有相同的、或修飾詞F。

MandFhavethesamenumberofparameters,andeachparameterinMhasthesameref,out,orinmodifiersasthecorrespondingparameterinF. 針對每個值參數(沒有ref、或修飾詞的參數outin)、識別轉換、隱含參考轉換或隱含指標轉換,都是從的參數類型M到中的對應參數類型F。

Foreachvalueparameter(aparameterwithnoref,out,orinmodifier),anidentityconversion,implicitreferenceconversion,orimplicitpointerconversionexistsfromtheparametertypeinMtothecorrespondingparametertypeinF. 針對每個ref、out或in參數,中的參數類型與M中的對應參數型別相同F。

Foreachref,out,orinparameter,theparametertypeinMisthesameasthecorrespondingparametertypeinF. 如果傳回型別是以value(no或),則會將傳回型別中的傳回型別refrefreadonly(identity、隱含參考或隱含指標轉換)存在於的傳回型別FM。

Ifthereturntypeisbyvalue(noreforrefreadonly),anidentity,implicitreference,orimplicitpointerconversionexistsfromthereturntypeofFtothereturntypeofM. 如果傳回型別是傳址(或),的傳回型別和修飾詞就會與的傳回型別和修飾詞refrefreadonlyrefF相同refM。

Ifthereturntypeisbyreference(reforrefreadonly),thereturntypeandrefmodifiersofFarethesameasthereturntypeandrefmodifiersofM. 的呼叫慣例與M的呼叫慣例相同F。

ThecallingconventionofMisthesameasthecallingconventionofF.這包括呼叫慣例位,以及未受管理識別碼中指定的任何呼叫慣例旗標。

Thisincludesboththecallingconventionbit,aswellasanycallingconventionflagsspecifiedintheunmanagedidentifier. M是一種靜態方法。

Misastaticmethod. 在unsafe內容中,EF如果E包含至少一個適用于其一般形式的方法,且其中至少包含一個方法,且該方法的一般形式適用于使用的參數類型和修飾詞所建立的引數清單(如下所F述),則會從位址的運算式中,將目標設為相容的函式指標類型的隱含轉換存在。

Inanunsafecontext,animplicitconversionexistsfromanaddress-ofexpressionwhosetargetisamethodgroupEtoacompatiblefunctionpointertypeFifEcontainsatleastonemethodthatisapplicableinitsnormalformtoanargumentlistconstructedbyuseoftheparametertypesandmodifiersofF,asdescribedinthefollowing. 選取單一方法,M對應至表單的方法調用E(A),並進行下列修改:AsinglemethodMisselectedcorrespondingtoamethodinvocationoftheformE(A)withthefollowingmodifications: 引數清單A是運算式的清單,每個都分類為變數,且具有的型別和修飾詞(ref、out或in)的對應funcptr_參數_清單F。

TheargumentslistAisalistofexpressions,eachclassifiedasavariableandwiththetypeandmodifier(ref,out,orin)ofthecorrespondingfuncptr_parameter_listofF. 候選方法只是適用于其一般格式的方法,而不是它們的擴充形式適用的方法。

Thecandidatemethodsareonlythosemethodsthatareapplicableintheirnormalform,notthoseapplicableintheirexpandedform. 候選方法只是靜態方法。

Thecandidatemethodsareonlythosemethodsthatarestatic. 如果多載解析演算法產生錯誤,則會發生編譯階段錯誤。

Ifthealgorithmofoverloadresolutionproducesanerror,thenacompile-timeerroroccurs.否則,此演算法M會產生與相同參數數目相同的單一最佳方法,F且會將轉換視為存在。

Otherwise,thealgorithmproducesasinglebestmethodMhavingthesamenumberofparametersasFandtheconversionisconsideredtoexist. 選取的方法M必須相容(如上面所定義的函式指標類型)F。

TheselectedmethodMmustbecompatible(asdefinedabove)withthefunctionpointertypeF.否則,會發生編譯時期錯誤。

Otherwise,acompile-timeerroroccurs. 轉換的結果是類型的函式指標F。

TheresultoftheconversionisafunctionpointeroftypeF. 這表示開發人員可以依賴多載解析規則來搭配使用address運算子:Thismeansdeveloperscandependonoverloadresolutionrulestoworkinconjunctionwiththeaddress-ofoperator: unsafeclassUtil{ publicstaticvoidLog(){} publicstaticvoidLog(stringp1){} publicstaticvoidLog(inti){}; voidUse(){ delegate*a1=&Log;//Log() delegate*a2=&Log;//Log(inti) //Error:ambiguousconversionfrommethodgroupLogto"void*" void*v=&Log; } Address運算子將會使用指令來執行ldftn。

Theaddress-ofoperatorwillbeimplementedusingtheldftninstruction. 這項功能的限制:Restrictionsofthisfeature: 只適用于標示為的方法static。

Onlyappliestomethodsmarkedasstatic. static不能在中使用非區域函數&。

Non-staticlocalfunctionscannotbeusedin&.這些方法的執行詳細資料不是由語言所指定。

Theimplementationdetailsofthesemethodsaredeliberatelynotspecifiedbythelanguage.這包括它們是否為靜態與實例,或是確切地發出的簽章。

Thisincludeswhethertheyarestaticvs.instanceorexactlywhatsignaturetheyareemittedwith. 函數指標類型上的運算子OperatorsonFunctionPointerTypes 在運算子的unsafe程式碼中,區段的修改方式如下:Thesectioninunsafecodeonoperatorsismodifiedassuch: 在unsafe內容中,有數個可在所有不_funcptrtype_s的_pointertype_s上操作的結構__:Inanunsafecontext,severalconstructsareavailableforoperatingonall_pointer_type_sthatarenot_funcptr_type_s: *運算子可用來執行指標間接)(指標間接取值。

The*operatormaybeusedtoperformpointerindirection(Pointerindirection). ->運算子可用來透過指標(指標成員存取)來存取結構的成員。

The->operatormaybeusedtoaccessamemberofastructthroughapointer(Pointermemberaccess). []運算子可用來編制指標(指標專案存取)的索引。

The[]operatormaybeusedtoindexapointer(Pointerelementaccess). 您&可以使用運算子來取得變數位址,()的operator運算子。

The&operatormaybeusedtoobtaintheaddressofavariable(Theaddress-ofoperator). ++和--運算子可用來遞增和遞減指標(指標遞增和遞減)。

The++and--operatorsmaybeusedtoincrementanddecrementpointers(Pointerincrementanddecrement). +和-運算子可用來執行指標算術(指標算術)。

The+and-operatorsmaybeusedtoperformpointerarithmetic(Pointerarithmetic). 、、、、==!=<><=和運算子可用=>來比較指標比較)(指標。

The==,!=,,<=,and=>operatorsmaybeusedtocomparepointers(Pointercomparison). stackalloc運算子可用來從呼叫堆疊(固定大小緩衝區)配置記憶體。

Thestackallocoperatormaybeusedtoallocatememoryfromthecallstack(Fixedsizebuffers). fixed語句可用來暫時修正變數,以便(fixed語句)取得其位址。

Thefixedstatementmaybeusedtotemporarilyfixavariablesoitsaddresscanbeobtained(Thefixedstatement). 在unsafe內容中,有數個可在所有_funcptrtype_s上運作的結構_:Inanunsafecontext,severalconstructsareavailableforoperatingonall_funcptr_type_s: &操作員可以用來取得靜態方法的位址(允許目標方法的位址)The&operatormaybeusedtoobtaintheaddressofstaticmethods(Allowaddress-oftotargetmethods) 、、、、==!=<><=和運算子可用=>來比較指標比較)(指標。

The==,!=,,<=,and=>operatorsmaybeusedtocomparepointers(Pointercomparison). 此外,我們會將中的所有區段修改Pointersinexpressions為禁止函式指標類型,除了Pointercomparison和之外Thesizeofoperator。

Additionally,wemodifyallthesectionsinPointersinexpressionstoforbidfunctionpointertypes,exceptPointercomparisonandThesizeofoperator. 更好的函式成員Betterfunctionmember 更好的函式成員規格將會變更,以包含下列程式程式碼:Thebetterfunctionmemberspecificationwillbechangedtoincludethefollowingline: delegate*比起更明確void*Adelegate*ismorespecificthanvoid* 這表示可以在和上多載,void*delegate*而且仍sensibly使用address運算子。

Thismeansthatitispossibletooverloadonvoid*andadelegate*andstillsensiblyusetheaddress-ofoperator. 類型推斷TypeInference 在unsafe程式碼中,類型推斷演算法會進行下列變更:Inunsafecode,thefollowingchangesaremadetothetypeinferencealgorithms: 輸入類型Inputtypes https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#input-types 新增下列內容:Thefollowingisadded: 如果E是位址方法群組,且為函式T指標類型,則的所有參數類型T都是具有類型的輸入類型ET。

IfEisanaddress-ofmethodgroupandTisafunctionpointertypethenalltheparametertypesofTareinputtypesofEwithtypeT. 輸出類型Outputtypes https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#output-types 新增下列內容:Thefollowingisadded: 如果E是傳址方法群組,且為函式指標類型,則的傳回T型別T會是類型為的輸出類型ET。

IfEisanaddress-ofmethodgroupandTisafunctionpointertypethenthereturntypeofTisanoutputtypeofEwithtypeT. 輸出類型推斷Outputtypeinferences https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#output-type-inferences 專案符號2和3之間會加入下列專案符號:Thefollowingbulletisaddedbetweenbullets2and3: 如果E是傳址方法群組,且T是具有參數型別和傳回型別的函式指標型別T1...TkTb,而且具有類型的多載解析(E具有類型T1..Tk)會產生具有傳回類型的單一方法U,則會從進行下限推斷UTb。

IfEisanaddress-ofmethodgroupandTisafunctionpointertypewithparametertypesT1...TkandreturntypeTb,andoverloadresolutionofEwiththetypesT1..TkyieldsasinglemethodwithreturntypeU,thenalower-boundinferenceismadefromUtoTb. 確切推斷Exactinferences https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#exact-inferences 下列子專案符號會以案例的形式新增至專案符號2:Thefollowingsub-bulletisaddedasacasetobullet2: V是函式指標型delegate*別U,而且是函式指標型別delegate*,而的呼叫慣例與V相同U,而且的refness與Vi相同Ui。

Visafunctionpointertypedelegate*andUisafunctionpointertypedelegate*,andthecallingconventionofVisidenticaltoU,andtherefnessofViisidenticaltoUi. 下限推斷Lower-boundinferences https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#lower-bound-inferences 下列案例會新增至專案符號3:Thefollowingcaseisaddedtobullet3: V是函式指標型別delegate*,而且有與相同的函式指標型別,而且的delegate*Udelegate*呼叫慣例與V相同U,而且的refness與Vi相同Ui。

Visafunctionpointertypedelegate*andthereisafunctionpointertypedelegate*suchthatUisidenticaltodelegate*,andthecallingconventionofVisidenticaltoU,andtherefnessofViisidenticaltoUi. 從到之推斷的第一個專案符號UiVi會修改為:ThefirstbulletofinferencefromUitoViismodifiedto: 如果不是函式U指標型別Ui,而且不知道是參考型別,或如果U是函式指標型別,而且Ui不知道函式指標型別或參考型別,則會進行確切的推斷IfUisnotafunctionpointertypeandUiisnotknowntobeareferencetype,orifUisafunctionpointertypeandUiisnotknowntobeafunctionpointertypeorareferencetype,thenanexactinferenceismade 然後,在推斷的第三個專案符號之後新增Ui至Vi:Then,addedafterthe3rdbulletofinferencefromUitoVi: 否則,如果V為,則delegate*推斷相依于的第i個參數delegate*:Otherwise,ifVisdelegate*theninferencedependsonthei-thparameterofdelegate*: 如果V1:IfV1: 如果傳回的值為,則會進行下限推斷。

Ifthereturnisbyvalue,thenalower-boundinferenceismade. 如果傳回以傳址方式傳回,則會進行完全推斷。

Ifthereturnisbyreference,thenanexactinferenceismade. 如果V2.。



VkIfV2..Vk: 如果參數是以傳值方式進行,則會進行上限的推斷。

Iftheparameterisbyvalue,thenanupper-boundinferenceismade. 如果參數是以傳址方式進行,則會進行完全推斷。

Iftheparameterisbyreference,thenanexactinferenceismade. 上限推斷Upper-boundinferences https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#upper-bound-inferences 下列案例會新增至專案符號2:Thefollowingcaseisaddedtobullet2: U是函式指標型別delegate*,而且是與相同的函式指標型別,而且的Vdelegate*呼叫慣例與U相同V,而且的refness與Ui相同Vi。

Uisafunctionpointertypedelegate*andVisafunctionpointertypewhichisidenticaltodelegate*,andthecallingconventionofUisidenticaltoV,andtherefnessofUiisidenticaltoVi. 從到之推斷的第一個專案符號UiVi會修改為:ThefirstbulletofinferencefromUitoViismodifiedto: 如果不是函式U指標型別Ui,而且不知道是參考型別,或如果U是函式指標型別,而且Ui不知道函式指標型別或參考型別,則會進行確切的推斷IfUisnotafunctionpointertypeandUiisnotknowntobeareferencetype,orifUisafunctionpointertypeandUiisnotknowntobeafunctionpointertypeorareferencetype,thenanexactinferenceismade 然後,在推斷的第三個專案符號之後新增Ui至Vi:Thenaddedafterthe3rdbulletofinferencefromUitoVi: 否則,如果U為,則delegate*推斷相依于的第i個參數delegate*:Otherwise,ifUisdelegate*theninferencedependsonthei-thparameterofdelegate*: 如果U1:IfU1: 如果傳回的值為,則會進行上限的推斷。

Ifthereturnisbyvalue,thenanupper-boundinferenceismade. 如果傳回以傳址方式傳回,則會進行完全推斷。

Ifthereturnisbyreference,thenanexactinferenceismade. 若為U2.。



英國:IfU2..Uk: 如果參數是以傳值方式進行,則會進行下限推斷。

Iftheparameterisbyvalue,thenalower-boundinferenceismade. 如果參數是以傳址方式進行,則會進行完全推斷。

Iftheparameterisbyreference,thenanexactinferenceismade. in、out和refreadonly參數和傳回類型的中繼資料標記法Metadatarepresentationofin,out,andrefreadonlyparametersandreturntypes 函式指標特徵標記沒有任何參數旗標位置,因此我們必須使用modreqs來編碼參數和傳回類型是in、out或refreadonly。

Functionpointersignatureshavenoparameterflagslocation,sowemustencodewhetherparametersandthereturntypearein,out,orrefreadonlybyusingmodreqs. in 我們重複使用System.Runtime.InteropServices.InAttribute,並modreq在參數或傳回類型上套用為ref規範,以表示下列各項:WereuseSystem.Runtime.InteropServices.InAttribute,appliedasamodreqtotherefspecifieronaparameterorreturntype,tomeanthefollowing: 如果套用至參數ref規範,則會將這個參數視為in。

Ifappliedtoaparameterrefspecifier,thisparameteristreatedasin. 如果套用至傳回型別ref規範,則會將傳回類型視為refreadonly。

Ifappliedtothereturntyperefspecifier,thereturntypeistreatedasrefreadonly. out 我們System.Runtime.InteropServices.OutAttribute會使用,modreq在參數類型上套用做為ref規範的,以表示參數為參數out。

WeuseSystem.Runtime.InteropServices.OutAttribute,appliedasamodreqtotherefspecifieronaparametertype,tomeanthattheparameterisanoutparameter. ErrorsErrors 將modreq套用至傳回型別時,會發生錯誤OutAttribute。

ItisanerrortoapplyOutAttributeasamodreqtoareturntype. 將InAttribute和OutAttribute作為modreq套用至參數類型時,會發生錯誤。

ItisanerrortoapplybothInAttributeandOutAttributeasamodreqtoaparametertype. 如果透過modopt指定任一項,則會忽略它們。

Ifeitherarespecifiedviamodopt,theyareignored. 呼叫慣例的中繼資料標記法MetadataRepresentationofCallingConventions 呼叫慣例會在簽章的方法簽章中,以簽章CallKind中的旗標和零或多個s的組合來編碼modopt。

CallingconventionsareencodedinamethodsignatureinmetadatabyacombinationoftheCallKindflaginthesignatureandzeroormoremodoptsatthestartofthesignature.ECMA-335目前宣告旗標中的下列元素CallKind:ECMA-335currentlydeclaresthefollowingelementsintheCallKindflag: CallKind :default |unmanagedcdecl |unmanagedfastcall |unmanagedthiscall |unmanagedstdcall |varargs ; 在這些情況下,c#中的函式指標將支援所有但varargs。

Ofthese,functionpointersinC#willsupportallbutvarargs. 此外,執行時間(和最後335)將會更新,以CallKind在新的平臺上包含新的。

Inaddition,theruntime(andeventually335)willbeupdatedtoincludeanewCallKindonnewplatforms.這並不是目前的正式名稱,但這份檔將用unmanagedext來做為新的可延伸呼叫慣例格式的替代預留位置。

Thisdoesnothaveaformalnamecurrently,butthisdocumentwilluseunmanagedextasaplaceholdertostandforthenewextensiblecallingconventionformat.如果沒有modopt,unmanagedext則為平臺預設呼叫慣例,unmanaged沒有方括弧。

Withnomodopts,unmanagedextistheplatformdefaultcallingconvention,unmanagedwithoutthesquarebrackets. 將對應calling_convention_specifier至CallKindMappingthecalling_convention_specifiertoaCallKind 如果calling_convention_specifier省略,或指定為,會managed對應至defaultCallKind。

Acalling_convention_specifierthatisomitted,orspecifiedasmanaged,mapstothedefaultCallKind.這是CallKind未使用屬性之任何方法的預設值UnmanagedCallersOnly。

ThisisdefaultCallKindofanymethodnotattributedwithUnmanagedCallersOnly. C#會辨識出從ECMA335對應至特定現有非受控的4個特殊識別碼CallKind。

C#recognizes4specialidentifiersthatmaptospecificexistingunmanagedCallKindsfromECMA335.為了進行這項對應,必須自行指定這些識別碼(沒有其他識別碼),而這項需求會編碼為的規格unmanaged_calling_convention。

Inorderforthismappingtooccur,theseidentifiersmustbespecifiedontheirown,withnootheridentifiers,andthisrequirementisencodedintothespecforunmanaged_calling_conventions.這些識別碼CdeclThiscallStdcallFastcall分別對應至、、和,分別對應unmanagedcdecl至unmanagedthiscallunmanagedstdcallunmanagedfastcall、、和。

TheseidentifiersareCdecl,Thiscall,Stdcall,andFastcall,whichcorrespondtounmanagedcdecl,unmanagedthiscall,unmanagedstdcall,andunmanagedfastcall,respectively.如果指定了一個以上的identifer,或單一不identifier是特別辨識的識別碼,我們就會在識別碼上執行特殊名稱查閱,並具有下列規則:Ifmorethanoneidentiferisspecified,orthesingleidentifierisnotofthespeciallyrecognizedidentifiers,weperformspecialnamelookupontheidentifierwiththefollowingrules: 我們在前面identifier加上字串CallConvWeprependtheidentifierwiththestringCallConv 我們只會查看命名空間中定義的類型System.Runtime.CompilerServices。

WelookonlyattypesdefinedintheSystem.Runtime.CompilerServicesnamespace. 我們只會查看應用程式核心程式庫中定義的類型,也就是定義且沒有相依性的程式庫System.Object。

Welookonlyattypesdefinedinthecorelibraryoftheapplication,whichisthelibrarythatdefinesSystem.Objectandhasnodependencies. 我們只探討公用類型。

Welookonlyatpublictypes. 如果查閱在中指定的所有identifier,則會將unmanaged_calling_conventionCallKind設定為,並針對函式指標簽章開頭的unmanagedext集合中的每個已解析類型進行編碼modopt。

Iflookupsucceedsonalloftheidentifiersspecifiedinanunmanaged_calling_convention,weencodetheCallKindasunmanagedext,andencodeeachoftheresolvedtypesinthesetofmodoptsatthebeginningofthefunctionpointersignature.要注意的是,這些規則表示使用者不能在這些中加上前置詞identifierCallConv,因為這樣會導致查詢CallConvCallConvVectorCall。

Asanote,theserulesmeanthatuserscannotprefixtheseidentifierswithCallConv,asthatwillresultinlookingupCallConvCallConvVectorCall. 在解讀中繼資料時,我們先看看CallKind。

Wheninterpretingmetadata,wefirstlookattheCallKind.如果不是unmanagedext,我們會忽略傳回型別上的所有,modopt以判斷呼叫慣例,並只使用CallKind。

Ifitisanythingotherthanunmanagedext,weignoreallmodoptsonthereturntypeforthepurposesofdeterminingthecallingconvention,anduseonlytheCallKind.如果CallKind是,我們會在函式unmanagedext指標類型的開頭查看modopts,並採用符合下列需求的所有類型聯集:IftheCallKindisunmanagedext,welookatthemodoptsatthestartofthefunctionpointertype,takingtheunionofalltypesthatmeetthefollowingrequirements: 是在核心程式庫中定義,也就是參考沒有其他程式庫和定義的程式庫System.Object。

Theisdefinedinthecorelibrary,whichisthelibrarythatreferencesnootherlibrariesanddefinesSystem.Object. 此類型定義于System.Runtime.CompilerServices命名空間中。

ThetypeisdefinedintheSystem.Runtime.CompilerServicesnamespace. 型別開頭為前置詞CallConv。

ThetypestartswiththeprefixCallConv. 此類型為public。

Thetypeispublic. 這些代表在identifierunmanaged_calling_convention定義來源中的函式指標類型時,在中的上執行查閱時必須找到的類型。

Theserepresentthetypesthatmustbefoundwhenperforminglookupontheidentifiersinanunmanaged_calling_conventionwhendefiningafunctionpointertypeinsource. CallKindunmanagedext如果目標執行時間不支援此功能,嘗試搭配使用函式指標時,就會發生錯誤。

ItisanerrortoattempttouseafunctionpointerwithaCallKindofunmanagedextifthetargetruntimedoesnotsupportthefeature.這會藉由尋找常數的存在而決定System.Runtime.CompilerServices.RuntimeFeature.UnmanagedCallKind。

ThiswillbedeterminedbylookingforthepresenceoftheSystem.Runtime.CompilerServices.RuntimeFeature.UnmanagedCallKindconstant.如果這個常數存在,則會將執行時間視為支援此功能。

Ifthisconstantispresent,theruntimeisconsideredtosupportthefeature. System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute是CLR所使用的屬性,表示應該使用特定的呼叫慣例來呼叫方法。

System.Runtime.InteropServices.UnmanagedCallersOnlyAttributeisanattributeusedbytheCLRtoindicatethatamethodshouldbecalledwithaspecificcallingconvention.基於這個原因,我們會介紹下列支援以使用屬性:Becauseofthis,weintroducethefollowingsupportforworkingwiththeattribute: 直接從c#呼叫以這個屬性批註的方法是錯誤的。

ItisanerrortodirectlycallamethodannotatedwiththisattributefromC#.使用者必須取得方法的函式指標,然後叫用該指標。

Usersmustobtainafunctionpointertothemethodandtheninvokethatpointer. 將屬性套用至一般靜態方法或一般靜態區域函式以外的任何元素時,會發生錯誤。

Itisanerrortoapplytheattributetoanythingotherthananordinarystaticmethodorordinarystaticlocalfunction. C#編譯器會標示從中繼資料匯入的任何非靜態或靜態非一般方法,此屬性是語言不支援的。

TheC#compilerwillmarkanynon-staticorstaticnon-ordinarymethodsimportedfrommetadatawiththisattributeasunsupportedbythelanguage. 以屬性標記的方法,具有不是的參數或傳回型別時,會發生錯誤unmanaged_type。

Itisanerrorforamethodmarkedwiththeattributetohaveaparameterorreturntypethatisnotanunmanaged_type. 標記有屬性的方法會有型別參數,即使這些型別參數受限於,也是錯誤unmanaged。

Itisanerrorforamethodmarkedwiththeattributetohavetypeparameters,evenifthosetypeparametersareconstrainedtounmanaged. 泛型型別中的方法會以屬性標記,是一項錯誤。

Itisanerrorforamethodinagenerictypetobemarkedwiththeattribute. 將以屬性標記的方法轉換為委派類型時,會發生錯誤。

Itisanerrortoconvertamethodmarkedwiththeattributetoadelegatetype. 針對UnmanagedCallersOnly.CallConvs不符合modopt在中繼資料中呼叫慣例的需求所指定的任何類型時,會發生錯誤。

ItisanerrortospecifyanytypesforUnmanagedCallersOnly.CallConvsthatdonotmeettherequirementsforcallingconventionmodoptsinmetadata. 當您決定以有效屬性標記之方法的呼叫慣例時UnmanagedCallersOnly,編譯器會對屬性中指定的型別執行下列檢查,CallConvs以判斷CallKindmodopt應該用來判斷呼叫慣例的有效和。

WhendeterminingthecallingconventionofamethodmarkedwithavalidUnmanagedCallersOnlyattribute,thecompilerperformsthefollowingchecksonthetypesspecifiedintheCallConvspropertytodeterminetheeffectiveCallKindandmodoptsthatshouldbeusedtodeterminethecallingconvention: 如果未指定任何類型,則CallKind會將視為,在函式unmanagedextmodopt指標類型的開頭沒有呼叫慣例s。

Ifnotypesarespecified,theCallKindistreatedasunmanagedext,withnocallingconventionmodoptsatthestartofthefunctionpointertype. 如果指定了一種類型,而且該類型命名為CallConvCdecl、CallConvThiscall、CallConvStdcall或CallConvFastcall,則CallKind會分別將視為、、或。

在函式unmanagedcdeclunmanagedthiscallunmanagedstdcallunmanagedfastcallmodopt指標類型的開頭沒有呼叫慣例s。

Ifthereisonetypespecified,andthattypeisnamedCallConvCdecl,CallConvThiscall,CallConvStdcall,orCallConvFastcall,theCallKindistreatedasunmanagedcdecl,unmanagedthiscall,unmanagedstdcall,orunmanagedfastcall,respectively,withnocallingconventionmodoptsatthestartofthefunctionpointertype. 如果指定了多個型別,或單一型別未命名上述的其中一個特別呼叫的out型別,則CallKind會將視為,並在函式unmanagedext指標型別開頭將所指定類型的聯集視為modopts。

Ifmultipletypesarespecifiedorthesingletypeisnotnamedoneofthespeciallycalledouttypesabove,theCallKindistreatedasunmanagedext,withtheunionofthetypesspecifiedtreatedasmodoptsatthestartofthefunctionpointertype. 編譯器接著會查看此有效CallKind和modopt集合,並使用標準中繼資料規則來判斷函式指標類型的最終呼叫慣例。

ThecompilerthenlooksatthiseffectiveCallKindandmodoptcollectionandusesnormalmetadatarulestodeterminethefinalcallingconventionofthefunctionpointertype. 未結問題OpenQuestions 偵測執行時間支援unmanagedextDetectingruntimesupportforunmanagedext https://github.com/dotnet/runtime/issues/38135追蹤新增此旗標。

https://github.com/dotnet/runtime/issues/38135tracksaddingthisflag.根據評論的意見反應,我們將使用問題中所指定的屬性,或使用出現的UnmanagedCallersOnlyAttribute作為旗標,判斷執行時間是否支援unmanagedext。

Dependingonthefeedbackfromreview,wewilleitherusethepropertyspecifiedintheissue,orusethepresenceofUnmanagedCallersOnlyAttributeastheflagthatdetermineswhethertheruntimessupportsunmanagedext. 考量Considerations 允許實例方法Allowinstancemethods 您可以利用EXPLICITTHISinstancec#程式碼)中所命名的CLI呼叫慣例(,擴充提案以支援實例方法。

TheproposalcouldbeextendedtosupportinstancemethodsbytakingadvantageoftheEXPLICITTHISCLIcallingconvention(namedinstanceinC#code).這種形式的CLI函式指標會將this參數放置為函式指標語法的明確第一個參數。

ThisformofCLIfunctionpointersputsthethisparameterasanexplicitfirstparameterofthefunctionpointersyntax. unsafeclassInstance{ voidUse(){ delegate*instancef=&ToString; f(this); } } 這聽起來很複雜,但會增加提案的一些複雜部分。

Thisissoundbutaddssomecomplicationtotheproposal.尤其是因為instancemanaged這兩個案例都是用來叫用具有相同c#簽章的managed方法,所以與呼叫慣例不同的函式指標也會不相容。

ParticularlybecausefunctionpointerswhichdifferedbythecallingconventioninstanceandmanagedwouldbeincompatibleeventhoughbothcasesareusedtoinvokemanagedmethodswiththesameC#signature.此外,在每個案例中,如果有一個簡單的因應措施,請考慮使用static區域函數。

Alsoineverycaseconsideredwherethiswouldbevaluabletohavetherewasasimpleworkaround:useastaticlocalfunction. unsafeclassInstance{ voidUse(){ staticstringtoString(Instancei)=>i.ToString(); delegate*f=&toString; f(this); } } 宣告時不需要unsafeDon'trequireunsafeatdeclaration 不需要unsafe在每次使用時都需要,而是delegate*在方法群組轉換成的點上只要求它delegate*。

Insteadofrequiringunsafeateveryuseofadelegate*,onlyrequireitatthepointwhereamethodgroupisconvertedtoadelegate*.這就是核心安全問題的播放(知道,當值)時,無法卸載包含的元件。

Thisiswherethecoresafetyissuescomeintoplay(knowingthatthecontainingassemblycannotbeunloadedwhilethevalueisalive).unsafe在其他位置上的要求可能會被視為過度。

Requiringunsafeontheotherlocationscanbeseenasexcessive. 這是設計原本的用途。

Thisishowthedesignwasoriginallyintended.但產生的語言規則覺得很麻煩。

Buttheresultinglanguagerulesfeltveryawkward.您無法隱藏這是指標值的事實,而且即使沒有關鍵詞,也不會繼續查看unsafe。

It'simpossibletohidethefactthatthisisapointervalueanditkeptpeekingthroughevenwithouttheunsafekeyword.例如,object無法允許轉換,它不能是的成員class等等。

C#設計是要求unsafe所有指標用途,因此這種設計會如下所示。

Forexampletheconversiontoobjectcan'tbeallowed,itcan'tbeamemberofaclass,etc...TheC#designistorequireunsafeforallpointerusesandhencethisdesignfollowsthat. 開發人員還是能夠在值的最上層呈現安全的包裝函式delegate*,就像是現今一般指標類型一樣。

Developerswillstillbecapableofpresentingasafewrapperontopofdelegate*valuesthesamewaythattheydofornormalpointertypestoday.考量:Consider: unsafestructAction{ delegate*_ptr; Action(delegate*ptr)=>_ptr=ptr; publicvoidInvoke()=>_ptr(); } 使用委派Usingdelegates 不使用新的語法元素,delegate*只要使用delegate具有下列類型的現有型別即可*:Insteadofusinganewsyntaxelement,delegate*,simplyuseexistingdelegatetypeswitha*followingthetype: Func*ptr=&object.ReferenceEquals; 藉由以指定值的屬性來標注型別,即可完成處理呼叫慣例delegateCallingConvention。

HandlingcallingconventioncanbedonebyannotatingthedelegatetypeswithanattributethatspecifiesaCallingConventionvalue.缺少屬性會代表managed呼叫慣例。

Thelackofanattributewouldsignifythemanagedcallingconvention. 在IL中編碼這項功能有問題。

EncodingthisinILisproblematic.基礎值必須以指標表示,但它也必須:Theunderlyingvalueneedstoberepresentedasapointeryetitalsomust: 具有唯一的型別,可允許具有不同函式指標類型的多載。

Haveauniquetypetoallowforoverloadswithdifferentfunctionpointertypes. 適用于跨元件界限的OHI用途。

BeequivalentforOHIpurposesacrossassemblyboundaries. 最後一點特別有問題。

Thelastpointisparticularlyproblematic.這表示,使用的每個元件都Func*必須在中繼資料中編碼相等的型別,即使Func*是在元件中定義的,但仍無法控制。

ThismeanthateveryassemblywhichusesFunc*mustencodeanequivalenttypeinmetadataeventhoughFunc*isdefinedinanassemblythoughdon'tcontrol. 此外,在不是mscorlib.dll的元件中,以名稱定義的任何其他類型,都System.Func必須與mscorlib.dll中定義的版本不同。

AdditionallyanyothertypewhichisdefinedwiththenameSystem.Funcinanassemblythatisnotmscorlibmustbedifferentthantheversiondefinedinmscorlib. 其中一個已探索的選項是發出像這樣的指標mod_req(Func)void*。

Oneoptionthatwasexploredwasemittingsuchapointerasmod_req(Func)void*.因為無法系結mod_req至TypeSpec,因此無法以泛型具現化為目標,所以無法運作。

Thisdoesn'tworkthoughasamod_reqcannotbindtoaTypeSpecandhencecannottargetgenericinstantiations. 命名函式指標Namedfunctionpointers 函式指標語法可能很繁瑣,特別是在複雜的情況下,例如嵌套的函式指標。

Thefunctionpointersyntaxcanbecumbersome,particularlyincomplexcaseslikenestedfunctionpointers.不是讓開發人員在每次語言可允許函式指標的命名宣告時都輸入簽章,就像是使用完成的一樣delegate。

Ratherthanhavedeveloperstypeoutthesignatureeverytimethelanguagecouldallowfornameddeclarationsoffunctionpointersasisdonewithdelegate. func*voidAction(); unsafeclassNamedExample{ voidM(Actiona){ a(); } } 這裡的問題部分是基礎CLI基本型別沒有名稱,因此這只是c#發明,而且需要一些中繼資料才能啟用。

PartoftheproblemhereistheunderlyingCLIprimitivedoesn'thavenameshencethiswouldbepurelyaC#inventionandrequireabitofmetadataworktoenable.這是雖可行的,但對工作而言是很重要的。

Thatisdoablebutisasignificantaboutofwork.基本上,c#需要c#與型別def資料表相關,才能使用這些名稱。

ItessentiallyrequiresC#tohaveacompaniontothetypedeftablepurelyforthesenames. 此外,當已檢查命名函式指標的引數時,我們發現它們可能同樣適用于許多其他案例。

Alsowhentheargumentsfornamedfunctionpointerswereexaminedwefoundtheycouldapplyequallywelltoanumberofotherscenarios.例如,宣告命名的元組,以降低在所有情況下輸入完整簽章的需求,是很方便的。

Forexampleitwouldbejustasconvenienttodeclarenamedtuplestoreducetheneedtotypeoutthefullsignatureinallcases. (intx,inty)Point; classNamedTupleExample{ voidM(Pointp){ Console.WriteLine(p.x); } } 在討論之後,我們決定不允許型別的命名宣告delegate*。

Afterdiscussionwedecidedtonotallownameddeclarationofdelegate*types.如果我們發現,根據客戶的使用意見反應,這項功能有很大的需求,我們將調查適用于函式指標、元組、泛型等的命名解決方案。

這可能類似于其他建議的格式,例如typedef語言的完整支援。

Ifwefindthereissignificantneedforthisbasedoncustomerusagefeedbackthenwewillinvestigateanamingsolutionthatworksforfunctionpointers,tuples,generics,etc...Thisislikelytobesimilarinformtoothersuggestionslikefulltypedefsupportinthelanguage. 未來的考量FutureConsiderations 靜態委派staticdelegates 這是指允許宣告類型的建議,而這些型別只能delegate參考static成員。

Thisreferstotheproposaltoallowforthedeclarationofdelegatetypeswhichcanonlyrefertostaticmembers.這類實例的優點是,這類delegate實例可以在效能敏感性案例中免費進行配置。

Theadvantagebeingthatsuchdelegateinstancescanbeallocationfreeandbetterinperformancesensitivescenarios. 如果實作為函式指標功能,則staticdelegate可能會關閉提案。

這項功能的建議優點是配置的免費本質。

Ifthefunctionpointerfeatureisimplementedthestaticdelegateproposalwilllikelybeclosedout.Theproposedadvantageofthatfeatureistheallocationfreenature.但最近的調查發現,由於元件卸載,無法達成此目的。

Howeverrecentinvestigationshavefoundthatisnotpossibletoachieveduetoassemblyunloading.必須有強式控制碼,才能從它所staticdelegate參考的方法,將元件從其下卸載。

Theremustbeastronghandlefromthestaticdelegatetothemethoditreferstoinordertokeeptheassemblyfrombeingunloadedoutfromunderit. 若要維護每個staticdelegate實例,必須配置新的控制碼,而此控制碼會對提案的目標執行計數器。

Tomaintaineverystaticdelegateinstancewouldberequiredtoallocateanewhandlewhichrunscountertothegoalsoftheproposal.在某些設計中,配置可分攤至每個呼叫網站的單一配置,但這有點複雜,而且似乎不值得權衡取捨。

Thereweresomedesignswheretheallocationcouldbeamortizedtoasingleallocationpercall-sitebutthatwasabitcomplexanddidn'tseemworththetradeoff. 這表示開發人員基本上必須決定下列各項取捨:Thatmeansdevelopersessentiallyhavetodecidebetweenthefollowingtradeoffs: 元件卸載的安全:這需要配置,因此delegate已經是足夠的選項。

Safetyinthefaceofassemblyunloading:thisrequiresallocationsandhencedelegateisalreadyasufficientoption. 元件卸載的臉部沒有安全性:使用delegate*。

Nosafetyinfaceofassemblyunloading:useadelegate*.這可以包裝在中struct,以允許在其餘程式unsafe代碼中的內容之外使用。

Thiscanbewrappedinastructtoallowusageoutsideanunsafecontextintherestofthecode. 本文內容



請為這篇文章評分?