Programim dhe zhvillim, javascript, python, php, html

një konstruktor si delegat - a është e mundur në C#?

Unë kam një klasë si më poshtë:

class Foo
{
  public Foo(int x) { ... }
}

dhe më duhet të kaloj në një metodë të caktuar një delegat si ky:

delegate Foo FooGenerator(int x);

A është e mundur të kalohet konstruktori drejtpërdrejt si një vlerë FooGenerator, pa pasur nevojë të shtypni:

delegate(int x) { return new Foo(x); }

?

EDIT: Për përdorimin tim personal, pyetja i referohet .NET 2.0, por sugjerimet/përgjigjet për 3.0+ janë gjithashtu të mirëseardhura.

21.10.2009

  • Pyetje interesante. Unë besoj se konstruktorët janë metoda efektive për sa i përket CLR-së, por unë nuk do ta dija sintaksën. 21.10.2009
  • Unë jam i interesuar: pse dëshironi ta bëni këtë? 21.10.2009
  • Megjithatë, dyshoj se përgjigja është jo. 21.10.2009
  • @Mitch Wheat: më shumë detaje: 1) Unë kisha klasa 'Deriv1' dhe 'Deriv2' që zgjeronin 'Foo', 2) kisha një metodë 'SomeLogic()' e cila ndonjëherë duhet të krijojë një objekt të prejardhur nga 'Foo' bazuar në 'x', 3) dhe doja të vendosja nëse kjo duhet të ishte 'Deriv1' ose 'Deriv2' kur thirrja metodën. Tani që e mendoj këtë, mund të dëshiroj të konsideroj përdorimin e një metode shabllon... por që kjo të jetë një përgjigje, më mirë duhet të hap një pyetje të veçantë :) +1 dhe gjithsesi lavdërime për ju :) 21.10.2009

Përgjigjet:


1

Jo, CLR nuk lejon delegatët e detyrueshëm për ConstructorInfo.

Sidoqoftë, thjesht mund të krijoni tuajën:

static T Make<T>(Action<T> init) where T : new()
{
  var t = new T();
  init(t);
  return t;
}

Përdorimi

var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });
21.10.2009
  • A mund të shtoni disa referenca (lidhje MSDN?) për deklaratën tuaj në lidhje me lidhjen me ConstructorInfo? 21.10.2009
  • Një konstruktor nuk prodhon një objekt të ri. Një Konstruktor punon në lidhje me një rutinë të alokimit. 21.10.2009
  • @sixlettervariables: +1 - që duket si një shpjegim i arsyeshëm. Thënë kështu, do të doja të shihja ende disa referenca MSDN/C#-specification/... nga dikush. 21.10.2009
  • @akavel: A duhet? msdn.microsoft.com/en-us/library/, gërmoni më thellë në Reflector për të parë që kërkon një RuntimeMethodHandle. 21.10.2009
  • ConstructorInfo != MethodInfo, prandaj një delegat nuk mund të krijohet nga një ConstructorInfo (pasi nuk është një metodë). 21.10.2009
  • Pranimi i përgjigjes për informacionin dhe komentet e lidhura me CLR. Faleminderit të gjithëve! 24.10.2009
  • Një zgjidhje tjetër, ndoshta e dyshimtë, është të pasqyroni të gjithë aktorët tuaj nëpërmjet një grupi metodash public static <SELF> Create<SELF>(/* args */). Prandaj mund të thuash Func<string, string, Foo> = Foo.CreateFoo. 14.02.2012
  • @JonathanDickinson - megjithatë, kjo duhet të bëhet për çdo klasë Foo që dëshironi të përdorni në këtë mënyrë. Pra, sa herë që mendoni se tani dua ta përdor këtë teknikë për class SomeClass, atëherë duhet të shtoni një metodë të tillë në përkufizimin e klasës, ose të shtoni një metodë shtesë në një klasë statike. Nuk po i shoh rrethanat në të cilat sugjerimi juaj do të ishte i preferueshëm sesa përgjigja e leppie, e cila thjesht funksionon, duke shfrytëzuar motorin e konkluzionit të përpiluesit. Përdorimi Foo.CreateFoo jo mjaftueshëm më i lehtë se Make<Foo>. 04.02.2017
  • @JonathanDickinson ... nga ana tjetër, ajo që ju sugjeroni është më afër formularit të përdorimit që kërkoi OP, ndaj faleminderit për këtë! 04.02.2017

  • 2

    Unë supozoj se normalisht do të bënit diçka të tillë si pjesë e një zbatimi në fabrikë, ku llojet aktuale nuk njihen në kohën e përpilimit...

    Së pari, vini re se një qasje më e lehtë mund të jetë një hap fillestar pas krijimit, atëherë mund të përdorni gjenerikët:

    static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
        T t = new T();
        t.Init(args);
        return t;
    }
    

    Më pas mund të përdorni MakeGenericMethod dhe/ose CreateDelegate.


    Përndryshe; ju mund ta bëni këtë me në fluturim me Expression (3.5) ose DynamicMethod (2.0).

    Qasja Expression është më e lehtë për tu koduar:

        var param = Expression.Parameter(typeof(int), "val");
        var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
        var lambda = Expression.Lambda<Func<int, Foo>>(
            Expression.New(ctor, param), param);
        var func = lambda.Compile();
        Foo foo = func(123);
        string s = foo.ToString(); // proof
    

    ose (duke përdorur DynamicMethod):

        ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
        DynamicMethod dm = new DynamicMethod("Create", typeof(Foo),
                new Type[] { typeof(int) }, typeof(Foo), true);
        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Newobj, ctor);
        il.Emit(OpCodes.Ret);
        Converter<int, Foo> func = (Converter<int, Foo>)
            dm.CreateDelegate(typeof(Converter<int, Foo>));        
        Foo foo = func(123);
        string s = foo.ToString(); // proof
    
    21.10.2009
  • Uh Oh; teknikisht, duke përdorur reflektimin etj. është e saktë (dhe e kam menduar edhe për një moment), por ka të meta serioze: 1) siç shihet në komentin tuaj, dëmton seriozisht lexueshmërinë (dhe e bën kodin më pak konciz në vend të më shumë); 2) AFAIK, reflektimi është më i ngadalshëm se konstruktet e mbështetura nga gjuha, pasi grumbullon një nivel më shumë abstraksioni. 21.10.2009
  • Pasi përpilohet te një delegat, një qasje e bazuar në reflektim nuk është më e ngadaltë dhe mund (nganjëherë) të jetë më e shpejtë se kodi i përpiluar i rregullt. Natyrisht, ju e përpiloni atë vetëm një herë dhe ruani delegatin. 21.10.2009
  • +1. Kjo (zbatimi i shprehjes) më ndihmoi më shumë sesa përgjigja e pranuar, pasi kisha nevojë për cctor, jo për ctor. 17.06.2011
  • +1 Kjo është padyshim më e mirë se përgjigja e pranuar! Faleminderit! 23.05.2012
  • Përgjigje e shkëlqyer, por nuk mund të mos them oh kur në Java mund të bëni Function<Integer, Foo> ctor = Foo::new. Dhe ju lutem mos më keqkuptoni, unë e urrej Java. 22.04.2020

  • 3

    Unë mendoj se aq koncize sa do të arrini (pa kaluar në një model fabrike) do të ishte diçka me metoda anonime, si kjo:

    delegate Foo FooGenerator(int x);
    
    ...    
    
    void DoStuff()
    {
        YourDelegateConsumer(x => new Foo(x));
    }
    

    Kjo nuk është duke bërë në mënyrë strikte atë që keni kërkuar (pasi ju po kaloni një delegat në një metodë anonime që kthen një shembull të ri, në vend që një delegat i drejtpërdrejtë te konstruktori), por nuk mendoj se çfarë po kërkoni është rreptësisht e mundur.

    Kjo, natyrisht, është duke supozuar se po përdorni 3.5+

    21.10.2009
  • +1; Unë në fakt jam duke përpiluar për 2.0, dhe kjo është arsyeja pse më është dashur të punoj me delegatin, por meqë pyetja thelbësore ka të bëjë me diçka tjetër, konstrukti lambda me siguri duhet të mbahet mend. 21.10.2009
  • Po, mendoj se kjo është mënyra më elegante dhe më pak e folur për ta bërë atë në C# moderne. E testuar, funksionon perfekt! +1 03.01.2021

  • 4

    Duket sikur ndoshta dëshironi të përdorni modelin e fabrikës së klasës.

    Modeli i metodës së fabrikës

    21.10.2009
  • Kjo ishte në fakt ajo që kisha përdorur si një zgjidhje, por për të shkruar pyetjen mendova se konstrukti 'delegat' ishte më i lehtë për t'u kuptuar. 21.10.2009
  • Modeli i metodës së fabrikës është i mirë, por vetëm nëse i njihni të gjitha llojet e mundshme në kohën e përpilimit. 12.01.2016

  • 5

    Fatkeqësisht jo, konstruktorët nuk janë të njëjtat gjëra si metodat dhe si të tillë nuk mund të krijosh një delegat që i drejton ata. Megjithatë, kjo është një ide interesante, ndoshta me më shumë informacion mund të krijojmë një lloj zgjidhjeje që do të ishte sintaksisht e ngjashme.

    21.10.2009
  • A mund të elaboroni pohimin se konstruktorët nuk janë plotësisht (...) si metoda në kontekstin e delegatëve? Do të doja veçanërisht disa referenca për referencën MSDN/C#/dokumentet e tjera. 21.10.2009
  • Përgjigje që nuk përgjigjet, i vetmi informacion i dhënë është 'Jo' dhe 'ndërtuesit nuk janë të njëjtat gjëra si metodat' (sigurisht jo). Po një përgjigje tjetër që pretendon të shpjegojë pse diçka nuk është e mundur duke thënë se thjesht nuk është. 28.03.2013

  • 6

    Përgjigja e Marc Gravell më frymëzoi për zgjidhjen e mëposhtme shumë të thjeshtë:

    static void Main()
    {
        Pet a = _MakeObject(typeof(Dog));
        Pet b = _MakeObject(typeof(Cat));
    }
    
    private static Pet _MakeObject(Type type)
    {
        ConstructorInfo info = type.GetConstructor(new Type[0]);
        return (Pet)info?.Invoke(null);
    }
    

    Pothuajse e njëjta gjë nëse konstruktori juaj ka parametra (në këtë shembull: 1 param i llojit int):

    static void Main()
    {
        Pet a = _MakeObject(typeof(Dog), 5);
        Pet b = _MakeObject(typeof(Cat), 7);
    }
    
    private static Pet _MakeObject(Type type, int age)
    {
        ConstructorInfo info = type.GetConstructor(new [] { typeof(int) });
        return (Pet)info?.Invoke(new object[] { age });
    }
    
    11.01.2016

    7

    Një tjetër opsion do të ishte përdorimi i klasës Activator, si kjo:

    Përdorimi i llojeve të përgjithshme

    public delegate Foo FooGeneratorDelegate<T>(int x);
    
    public T FooGeneratorFunction<T>(int x)
    {
        return (T)Activator.CreateInstance(typeof(T), x);
    }
    
    // implementation example
    FooGeneratorDelegate<Foo> del = FooGeneratorFunction<Foo>;
    Foo foo = del(someIntValue);
    

    Të kalosh Type Foo si parametër

    public delegate object FooGeneratorDelegate(Type t, int x);
    
    public object FooGeneratorFunction(Type t, int x)
    {
        return Activator.CreateInstance(t, x);
    }
    
    // implementation example
    FooGeneratorDelegate del = FooGeneratorFunction;
    Foo foo = (Foo)del(typeof(Foo), someIntValue);
    

    Nëse lloji do të jetë gjithmonë i llojit Foo

    public delegate Foo FooGeneratorDelegate(int x);
    
    public Foo FooGeneratorFunction(int x)
    {
        return (Foo)Activator.CreateInstance(typeof(Foo), x);
    }
    
    // implementation example
    FooGeneratorDelegate del = FooGeneratorFunction;
    Foo foo = del(someIntValue);
    
    23.09.2019

    8

    Supozimi im është se nuk është e mundur pasi do të kaloni një metodë të një objekti që nuk është krijuar ende.

    21.10.2009

    9

    Ndërsa përgjigjet e tjera mbështesin vetëm konstruktorin pa parametra. Këtu janë parametrat e mbështetjes së versionit. Shpresoj se mund të ndihmojë dikë që dëshiron të kthejë me parametrat ConsturctorInfoDelegate.

    public Delegate CreateDelegate(ConstructorInfo constructorInfo)
    {
        var ctorParameters = constructorInfo.GetParameters();
        var lambdaParameters =
            ctorParameters.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray();
        var argsExp =
            ctorParameters.Select((p, i) => Expression.Convert(lambdaParameters[i], p.ParameterType));
        var newExp = Expression.New(constructorInfo, argsExp);
        var lambda = Expression.Lambda(newExp, lambdaParameters);
        return lambda.Compile();
    }
    

    Përdorimi:

    CreateDelegate(/* ConstructorInfo */).DynamicInvoke(args);
    
    18.03.2021
    Materiale të reja

    Masterclass Coroutines: Kapitulli-3: Anulimi i korutinave dhe trajtimi i përjashtimeve.
    Mirë se vini në udhëzuesin gjithëpërfshirës mbi Kotlin Coroutines! Në këtë seri artikujsh, unë do t'ju çoj në një udhëtim magjepsës, duke filluar nga bazat dhe gradualisht duke u thelluar në..

    Faketojeni derisa ta arrini me të dhënat false
    A e gjeni ndonjëherë veten duke ndërtuar një aplikacion të ri dhe keni nevojë për të dhëna testimi që duken dhe duken më realiste ose një grup i madh të dhënash për performancën e ngarkesës...

    Si të përdorni kërkesën API në Python
    Kërkesë API në GitHub për të marrë depot e përdoruesve duke përdorur Python. Në këtë artikull, unë shpjegoj procesin hap pas hapi për të trajtuar një kërkesë API për të marrë të dhëna nga..

    Një udhëzues hap pas hapi për të zotëruar React
    Në këtë artikull, do të mësoni se si të krijoni aplikacionin React, do të mësoni se si funksionon React dhe konceptet thelbësore që duhet të dini për të ndërtuar aplikacione React. Learning..

    AI dhe Psikologjia — Pjesa 2
    Në pjesën 2 të serisë sonë të AI dhe Psikologji ne diskutojmë se si makineritë mbledhin dhe përpunojnë të dhëna për të mësuar emocione dhe ndjenja të ndryshme në mendjen e njeriut, duke ndihmuar..

    Esencialet e punës ditore të kodit tim VS
    Shtesat e mia të preferuara - Git Graph 💹 Kjo shtesë është vërtet e mahnitshme, e përdor përpara se të filloj të punoj për të kontrolluar dy herë ndryshimet dhe degët më të fundit, mund të..

    Pse Python? Zbulimi i fuqisë së gjithanshme të një gjiganti programues
    Në peizazhin gjithnjë në zhvillim të gjuhëve të programimit, Python është shfaqur si një forcë dominuese. Rritja e tij meteorike nuk është rastësi. Joshja e Python qëndron në thjeshtësinë,..