CLR «функция көрсеткіші» мәнінің түрін қолдай ма еді?

Бірнеше күн бұрын, делегаттардың сілтеме түрлерінің себебі болып табылады , менің дұрыс емес түсінікке негізделген делегат үшін қажет барлық екі сілтеме: біреуі нысанға, екіншісі функцияға. Не толықтай бас тартқанымды (мені ұмытып кеткендіктен емес, себебі мен ұмытып кеткенімді емес) .NET-де делегаттар оқиғаларды қолдау үшін ішінара болып табылады, шақыру тізімі арқылы әрбір делегат бірнеше жазылушыны қолдайды дегенді білдіреді Observer pattern /em>.

Бұл маған ойлануға мүмкіндік берді, делегаттар шын мәнінде .NET әлемінде екі түрлі рөл атқарады. Біреуі кішіпейіл функция көрсеткішінің мысалы:

Action writeLine = Console.WriteLine;

басқа байқалатын:

textBox.TextChanged += HandleTextChanged;

Сәйкестік тізімінің болуы екінші рөл үшін ғана көрінеді, себебі қарапайым writeLine мысалында, әдетте, абоненттер туралы тіпті ойлауға да болмайды.

Мәселен, менің ойымша, мүмкін делегаттардың екі түрлі «түрін»: «функция көрсеткіші» және «байқалатын» түрі. Бірінші, меніңше, құндылық түрі болуы мүмкін.

Енді, егер бұл мүмкін болса, бұл болуға тиіс деп талпынбаймын. Делегаттардың құндылық түрлері болса, жаңа кілт сөзді ( multicast ?), сөзсіз әзірлеушілердің шатастыруы және т.б. Мен білгім келетін нәрсе, CLR тұрғысынан, функция ретінде әрекет ететін мән түріне ие болу үшін, егер көрсеткіш.

Менің болжауымша, бұл басқа әдіс болады: System.Delegate , оның шақыру тізімімен және барлығы, негізінен іргелі CLR түрі; не жай CLR тілдеріне әсер етпейтін қарапайым «функция сілтемесі» айналасында орамал ма?

Мен пайдаланған бейресми терминдердің барлығына кешірім сұраймын, олар онда білімді әзірлеушілердің кейбірін шатастырып жіберуі мүмкін.

11

4 жауаптар

CLR-тың алғашқы күндерінде System.Delegate (функционалдық сілтегіш) және System.MulticastDelegate (оқиға сияқты) арасында айырмашылық болған. Бұл бұрын жіберілген .NET 1.0-ге көшірілген, делегаттан туындайтын делегаттың үлгісін жасауға ешқандай мүмкіндік жоқ. Бұл қажет емес еді. MulticastDelegate бірден көп абонент болған кезде ғана шақыру тізімін жасау үшін оңтайландырылды. System.Delegate қандай да бір себептермен сақталған, оны алып тастау үшін тым көп жұмыс жасалуы мүмкін.

11
қосылды
Бұл System.Delegate бастапқы түрін мәні түріне айналдырды, немесе керісінше, subclassed мәндерінің түрлері бойынша болуы мүмкін екенін білдіреді, System.Enum сияқты көп нәрсе бар ма? (Шамасы, бұл әлі де емес еді бар, бірақ, мүмкін, көп себептер бойынша ойлаймын қарағанда?)
қосылды автор Dan Tao, көзі
Erm, қиын, бұл MulticastDelegate үшін негізгі сынып. Шындығында ешқашан болмаған нәрсе туралы ойлау қиын.
қосылды автор Hans Passant, көзі

Менің ойымша, көпшілік жағдайларда, .NET әзірлеушілері біртұтастығы/көпжолғы және жабық/ашық данасы үшін «бірыңғай» қолдау табады, ол шағын топтарға жұмсалады. Азшылыққа түскен жағдайда, өкінішке орай, егер сіз идиломдарды бұзып, көп дөңгелектерді қайта шығаруға қарсы болмасаңыз, айналаңыздағы жолдар бар. Кейінірек бұл туралы көбірек білу қажет.

Шын мәнінде, мәселенің мақсаты - айқын емес, бірақ мен жеке мәселелерді шешуге тырысамын.

Мен шынымен білгім келетін нәрсе, мүмкін болса,   CLR перспективасынан, а. ретінде әрекет ететін мән түріне ие болу үшін   функция көрсеткіші.

Әрине. Іс жүзінде, делегаттар көмегімен жергілікті ішкі өлшемдегі мән-түрдегі функция-көрсеткіштер ( IntPtr ) басқарылатын дүниеде пайдаланылады. Барлық түрдегі қауіпсіз қоңыраулар мен ысқырықтар үстіне салынған.

The IL for your Action writeLine = Console.WriteLine; example looks something like this:

// Push null-reference onto stack.
// (Console.WriteLine is a static method)
ldnull

// Push unmanaged pointer to desired function onto stack.
ldftn void [mscorlib]System.Console::WriteLine(string)

// Create delegate and push reference to it onto stack.
instance void [mscorlib]System.Action`1::.ctor(object, native int)

// Pop delegate-reference from top of the stack and store in local.
stloc.0 

where the Action constructor is very conveniently declared as:

// First arg is 'this' for closed-instance delegates.
// Second arg is pointer to function.
public Action(object @object, IntPtr method);

Менің болжауымша, бұл басқа болады: System.Delegate, with   оның шақыру тізімі және барлығы негізінен іргелі CLR түрі; немесе бар   бұл қарапайым «функционалдық сілтеме» түріндегі айналма қаптама   CLR тілдеріне әсер етпейді?

және «функционалдық анықтамалық» айналасында «қабық» деп аталатын CLR типті екеуі де - System.Delegate - көрсеткішті функцияға өріс ретінде сақтайды (жабық делдалдарға арналған нысан сілтемесі). Multicast-support, оның subclass System.MulticastDelegate арқылы, мультикастты шақыру тізімін сақтау қажеттілігіне байланысты, әлдеқайда күрделі. Біріктіру жабайы табиғаттағы барлық делегат-кодтар System.MulticastDelegate -тен мұрагер болуы керек.

Сонымен қатар, CLR тілдеріне « функциясының сілтемесі - делгендердің Target арқылы әдісті білдіретін MethodInfo қасиет және сол жерден байланысты әдіс-таңбалауыш және функция көрсеткіші.


Бірақ сіз мұның бәрін білсеңіз керек. Менің ойымша, real сұрағыңыз сияқты:

Жеңіл, типті қауіпсіз, делегат сияқты мән түрін салу керек еді   

. басқарылатын функцияға көрсеткіштен басқа ештеңе сақтамайды

Мен айтқанымның бәрін ескере отырып, бұл өте оңай:

// Cool but mostly useless lightweight (in space, not time)
// type-safe delegate-like value-type. Doesn't support closed-instance scenarios 
// in the interests of space, but trivial to put in if desired.
public struct LeanDelegate<tdelegate>
{
   //The only storage required.
    private readonly IntPtr _functionPointer;

    public LeanDelegate(TDelegate source)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        var del = source as Delegate;

        if (del == null)
            throw new ArgumentException("Argument is not a delegate", "source");

        if (del.Target != null)
            throw new ArgumentException("Delegate is a closed-instance delegate.", "source");

        if (del.GetInvocationList().Length > 1)
            throw new ArgumentException("Delegate is a multicast delegate.", "source");

        //Retrieve and store pointer to the delegate's target function.
        _functionPointer = del.Method.MethodHandle.GetFunctionPointer();
    }

   //Creates delegate-instance on demand.
    public TDelegate Delegate
    {
        get
        {
            if (_functionPointer == IntPtr.Zero)
                throw new InvalidOperationException("Uninitialized LeanDelegate instance.");

           //Use the aforementioned compiler-generated constructor 
           //to generate the delegate instance.
            return (TDelegate)Activator.CreateInstance
                              (typeof(TDelegate), null, _functionPointer);
        }
    }
}

Сонда сіз жасай аласыз:

 var del = new LeanDelegate>(Console.WriteLine);
 del.Delegate("Hello world");

What have you gained? The ability to store a pointer to an arbitrary static-method (or instance-method if the delegate is an open-instance one) and execute it in a type-safe manner, all in machine-pointer size space (excluding temporary allocations).

What have you lost? Closed-instance capability. Multicast. Direct compatibility with many other APIs. Speed (almost certainly), although workarounds are conceivable. The love of developers who will use your API.

4
қосылды
Мен Delegate-тің жалпы интерфейс түрлерінің отбасы болғанын қалаймын. Бұл әдіс деп аталатын делегат ретінде пайдалану үшін белгілі бір қолтаңбасы бар бір әдіске тек «делегат» жасауы керек болатын кез келген сыныпқа мүмкіндік берер еді. Бұл әсіресе жабылған заттарға пайдалы. Әйтпесе, делегат атомдық тапсырмаларды орындау үшін құрылым емес, класс болуы керек, бірақ делегаттардың көпшілігі бір мақсатқа ие болса, делегаттарға шақыру тізіміне қосымша өріс болуы үшін ешқандай себеп жоқ.
қосылды автор supercat, көзі
Мүмкін, мен мұқият емеспін, себебі мен MulticastDelegates-ді Observer үлгісіне пайдалануды шынымен жек көргендіктен. Оқиға жазылудың көп бөлігі және жазылудан шығарылған код өте қауіпсіз болып табылмайды, яғни сыртқы кодпен жарияланған оқиғаларға жазылу асинхронды контекстте жазылудан аулақ бола алмайды.
қосылды автор supercat, көзі
Құрылымға MethodInfo сақтауға болатынына назар аударыңыз. Әдіс-инфосы кэштелгендіктен, бұл ұқсас кеңістік профилін ұсынуы керек. Бірақ сіз нақты, шыншылдықты жақсылық функциясы көрсеткішін алғыңыз келеді.
қосылды автор Ani, көзі

Мұнда бір мәселе - атрибуция. Егер сіз мақсатқа және әдіске сілтеме жасайтын құрылымды ойласаңыз. Бұл құрылым екі көрсеткішке ие. CLR тек көрсеткіш өлшемі айнымалысы үшін атом жазбаларына кепілдік береді, сондықтан бұл мақсат пен әдісті синхрондау үшін кейбір арнайы механизмдерді қажет етеді. Бірақ егер шабуылдаушы түрі сәйкес келмейтін нысанға функцияны шақыра бастаса, ол құм жәшігін айналып өтуі мүмкін.

3
қосылды
Wow, өте жақсы нәрсе! Мен осы мәселе туралы ойламаған едім.
қосылды автор Dan Tao, көзі
Бұл тек тек себебі Делегаттың мәні болуы керек, бірақ бұл жеткілікті. Мінез-құлықтың функциясы бар түрдің ешқандай себебі жоқ және көп мақсатты өкілдерді қолдау үшін мақсат жеткіліксіз, бірақ атомдық емес жаңартулар өлтіруші болар еді.
қосылды автор supercat, көзі
Әрине. Мүмкін, «... сілтеме түріне айналуы керек» және «... құндылық түрі бола алмайды» деп пікірталасып, екі ұғымды бірге нашарлады. Рахмет. Ең бастысы, мультикастатты делегаттарға қолдау көрсету делегаттың қосымша өрістерін қажет етпейді. Көптеген делегаттардың әдістерін және мақсаттарын ұстап тұратын MultiDelegateInvoker сыныбын анықтау арқылы бірнеше делегаттарға қосылуға болады, содан кейін MultiDelegateInvoker нысаны болып табылатын және оның әдісі MultiDelegateInvoker бағдарламасындағы барлық делегаттарды іске қосатын жаңа делегатты жасай аласыз.
қосылды автор supercat, көзі
Мүмкін, оқиға жазылымдарын өңдеудің ең тиімді жолы болмайды, бірақ мультикаст өкілдері шынымен дұрыс көзқарас болмағандықтан, мен оны жоғалту ретінде көрмеймін. Многокомасштабты делегаттарды өңдеудің басқа әдістері кез келген жағдайда тиімдірек болады.
қосылды автор supercat, көзі
@supercat «Бұл Делегаттың құндылық түрі болуы керек жалғыз себебі» Сіз анықтамалық үлгі болуға тиіспіз бе? «
қосылды автор CodesInChaos, көзі

- System.Delegate , оның шақыру тізімі және барлығы, негізінен іргелі CLR түрі; немесе қандай да бір CLR тілдеріне әсер етпейтін қарапайым «функция сілтемесі» айналасында орамал ма?

Мен де деп ойлаймын. Біріншіден, System.Delegate CLR арқылы орындалатын кейбір әдістер болса да, оның даналары тек C# және басқа тілдерде (бұл тек Invoke әдісі бар сынып) және басқалары). Екіншіден, функционалдық көрсеткішті білдіретін fnptr деп аталатын нақты CLR түрі бар ( бұл сұрақты қараңыз ). Дегенмен, C# түрінде емес, сонымен қатар CIL ( method void *() ) немесе C ++/CLI ( void (* foo)() .

0
қосылды