Vra

Wat is die verskille tussen die afgevaardigdes en'n gebeure?Moenie albei hou verwysings na die funksies wat uitgevoer kan word?

Was dit nuttig?

Oplossing

'n Event verklaring voeg 'n laag van abstraksie en beskerming op die afgevaardigde byvoorbeeld. Hierdie beskerming verhoed kliënte van die afgevaardigde van die Herstel van die afgevaardigde en sy aanroeping lys en net laat die toevoeging of verwydering van teikens van die aanroeping lys.

Ander wenke

In bykomend tot die sintaktiese en operasionele eienskappe, daar is ook 'n semantiese verskil.

Afgevaardigdes is, konseptueel, funksie templates; dit wil sê, hulle uit te druk 'n kontrak 'n funksie moet voldoen om in aanmerking te kom van die "soort" van die afgevaardigde.

Events verteenwoordig ... wel, gebeure. Hulle is bedoel om iemand te waarsku wanneer iets gebeur en ja, hulle voldoen aan 'n afgevaardigde definisie maar hulle is nie dieselfde ding nie.

Selfs as hulle presies dieselfde ding (sintakties en in die IL-kode) sal daar nog steeds die semantiese verskil. Oor die algemeen verkies ek om twee verskillende name vir twee verskillende konsepte het, selfs al is dit geïmplementeer word op dieselfde wyse (wat nie beteken ek graag dieselfde kode twee keer het).

Om te verstaan die verskille wat jy kan kyk na hierdie 2 voorbeelde

Voorbeeld met Afgevaardigdes (in hierdie geval, 'n Aksie - dit is'n soort van die afgevaardigde wat nie terugkeer'n waarde)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

Gebruik die afgevaardigde, moet jy doen iets soos hierdie:

Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

Hierdie kode werk goed, maar jy kan'n paar swak plekke.

Byvoorbeeld, as ek skryf hierdie:

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

met die laaste reël van die kode, ek het geneutraliseer die vorige gedrag net met een vermiste + (Ek gebruik het = in plaas van +=)

Nog'n swak plek is dat elke klas wat gebruik maak van jou Animal die klas kan verhoog RaiseEvent net noem dit animal.RaiseEvent().

Om te verhoed dat hierdie swak plekke wat jy kan gebruik events in c#.

Jou Dier klas sal verander in hierdie manier:

public class ArgsSpecial : EventArgs
{
    public ArgsSpecial (string val)
    {
        Operation=val;
    }

    public string Operation {get; set;}
} 

public class Animal
{
    // Empty delegate. In this way you are sure that value is always != null 
    // because no one outside of the class can change it.
    public event EventHandler<ArgsSpecial> Run = delegate{} 

    public void RaiseEvent()
    {  
         Run(this, new ArgsSpecial("Run faster"));
    }
}

om te bel die gebeure

 Animal animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

Verskille:

  1. Jy is nie die gebruik van'n openbare eiendom, maar'n openbare gebied (met behulp van die gebeure, die samesteller beskerm jou velde van ongewenste toegang)
  2. Gebeure kan nie toegeken word direk.In hierdie geval, dit sal nie aanleiding gee tot die vorige fout wat ek het met die oorheersende gedrag.
  3. Niemand buite jou klas kan verhoog die gebeurtenis.
  4. Gebeure kan ingesluit word in'n koppelvlak verklaring, terwyl'n veld nie

Notas:

Eventhandler'is verklaar as die volgende delegeer:

public delegate void EventHandler (object sender, EventArgs e)

dit neem'n sender (van die Voorwerp tipe) en gebeurtenis argumente.Die sender is nietig indien dit kom van statiese metodes.

Hierdie voorbeeld, wat gebruik maak van EventHandler<ArgsSpecial>, kan ook geskryf word met behulp van EventHandler in plaas.

Verwys hier vir dokumentasie oor Eventhandler'

Hier is nog'n goeie skakel om te verwys na.http://csharpindepth.com/Articles/Chapter2/Events.aspx

Kortliks, die weg te neem van die artikel - die Gebeure is inkapseling oor afgevaardigdes.

Aanhaling uit die artikel:

Veronderstel gebeure nie bestaan het nie as'n konsep in C#/.NETTO.Hoe sou'n ander klas skryf aan'n gebeurtenis?Drie opsies:

  1. 'n openbare afgevaardigde veranderlike

  2. 'n afgevaardigde veranderlike gerugsteun deur'n eiendom

  3. 'n afgevaardigde veranderlike met AddXXXHandler en RemoveXXXHandler metodes

Opsie 1 is duidelik aaklige, vir al die normale redes ons het'n afsku van openbare veranderlikes.

Opsie 2 is effens beter, maar kan intekenaars om effektief te ignoreer mekaar - sou dit al te maklik om te skryf someInstance.MyEvent = eventhandler';wat sou vervang enige bestaande geleentheid hanteerders eerder as die toevoeging van'n nuwe een.Daarbenewens, het jy nog nodig het om te skryf die eienskappe.

Opsie 3 is basies wat die gebeure gee, maar met'n gewaarborgde konvensie (wat gegenereer word deur die samesteller en gerugsteun deur ekstra vlae in die IL) en'n "vrye" die implementering as jy gelukkig is met die semantiek wat veld-soos gebeure gee.Teken aan en uit te teken van die gebeure is vervat sonder dat arbitrêre toegang tot die lys van die gebeurtenis hanteerders, en tale kan maak dinge makliker deur die verskaffing van sintaksis vir beide verklaring en inskrywing.

Nota: As jy toegang tot C # 5,0 Unleashed het, lees die "Beperkings op Plain gebruik van Afgevaardigdes" in Hoofstuk 18 getiteld "Events" om 'n beter die verskille tussen die twee verstaan.


Dit help altyd vir my 'n eenvoudige, konkrete voorbeeld het. So hier is een vir die gemeenskap. Aanvanklik het ek wys hoe jy afgevaardigdes alleen kan gebruik om te doen wat Events doen vir ons. Dan wys ek hoe dieselfde oplossing sal werk met 'n geval van EventHandler. En dan het ek verduidelik waarom ons nie wil hê om te doen wat ek verduidelik in die eerste voorbeeld. Hierdie post is geïnspireer deur 'n artikel deur John kleiduiven.

Voorbeeld 1: Gebruik openbare afgevaardigde

As ek 'n Winforms app met 'n enkele drop-down box. Die drop-down is gebind om 'n List<Person>. Waar iemand eienskappe van ID, naam, bynaam Haarkleur. Op die belangrikste vorm is 'n persoonlike gebruiker beheer wat die eienskappe van daardie persoon toon. Wanneer iemand 'n persoon kies in die drop-down die etikette in die gebruiker beheer update na die eienskappe van die gekose persoon te wys.

betree beeld beskrywing hier

Hier is hoe dit werk. Ons het drie lêers wat ons help om te sit dit saam:

  • Mediator.cs - statiese klas hou die afgevaardigdes
  • Form1.cs - hoof vorm
  • DetailView.cs - gebruiker beheer toon al die besonderhede

Hier is die relevante kode vir elk van die klasse:

class Mediator
{
    public delegate void PersonChangedDelegate(Person p); //delegate type definition
    public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
    public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
    {
        if (PersonChangedDel != null)
        {
            PersonChangedDel(p);
        }
    }
}

Hier is ons gebruikers beheer:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.PersonChangedDel += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(Person p)
    {
        BindData(p);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

Ten slotte het ons die volgende kode in ons Form1.cs. Hier het ons 'n beroep OnPersonChanged, wat enige kode ingeskryf is by die afgevaardigde noem.

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}

Ok. So dit is hoe jy hierdie werk sonder die gebruik van gebeure sou kry en net die gebruik van afgevaardigdes . Ons het net 'n openbare verteenwoordiger in 'n klas - jy kan dit staties of 'n Singleton, of wat ook al maak. Groot.

MAAR, MAAR MAAR, ons wil nie om te doen wat ek net hierbo beskryf. Omdat openbare velde is sleg vir baie, baie rede. So, wat is ons opsies? Soos John kleiduiven beskryf, hier is ons opsies:

  1. 'n openbare verteenwoordiger veranderlike (dit is wat ons net bo het. Moenie dit doen nie. Ek het jou net bokant waarom dit sleg vertel)
  2. Plaas die afgevaardigde in 'n eiendom met 'n get / set (probleem hier is dat intekenaars mekaar kan ignoreer - sodat ons kan 'n klomp van die maniere om die afgevaardigde skryf en dan kan ons per ongeluk sê PersonChangedDel = null, uitroei al die . ander subskripsies die ander probleem wat hier bly is dat sedert die gebruikers toegang tot die afgevaardigde het, kan hulle die teikens te roep in die aanroeping lys - ons wil nie eksterne gebruikers toegang tot wanneer ons gebeure in te samel
  3. 'n afgevaardigde veranderlike met AddXXXHandler en RemoveXXXHandler metodes

Dit is die derde opsie is in wese wat 'n gebeurtenis gee ons. Wanneer ons 'n Eventhandler verklaar, dit gee ons toegang tot 'n afgevaardigde -. Nie publiek, nie as 'n eiendom nie, maar as hierdie ding noem ons 'n gebeurtenis wat net het byvoeg / verwyder Toegangers

Kom ons kyk wat dieselfde program lyk, maar nou met behulp van 'n Event in plaas van die publiek afgevaardigde (Ek het ook verander ons Middelaar 'n Singleton):

Voorbeeld 2: Met Eventhandler in plaas van 'n openbare verteenwoordiger

Middelaar:

class Mediator
{

    private static readonly Mediator _Instance = new Mediator();

    private Mediator() { }

    public static Mediator GetInstance()
    {
        return _Instance;
    }

    public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.

    public void OnPersonChanged(object sender, Person p)
    {
        var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
        if (personChangedDelegate != null)
        {
            personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
        }
    }
}

Let daarop dat as jy F12 op die Eventhandler, sal dit wys jou die definisie is net 'n generiese-ified afgevaardigde met die ekstra "sender" voorwerp:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

Die User Control:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
    {
        BindData(e.Person);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

Ten slotte, hier is die Form1.cs kode:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
        Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}

As gevolg van die Eventhandler wil en EventArgs as 'n parameter, ek het hierdie klas met net 'n enkele eiendom in dit:

class PersonChangedEventArgs
{
    public Person Person { get; set; }
}

Hopelik wat wys jy 'n bietjie oor hoekom ons gebeure en hoe dit verskil - maar funksioneel dieselfde -. As afgevaardigdes

Jy kan ook gebeure gebruik in koppelvlak verklarings, nie so vir afgevaardigdes.

Wat 'n groot misverstand tussen gebeure en afgevaardigdes !!! A afgevaardigde spesifiseer 'n tipe (soos 'n class, of 'n interface doen), terwyl 'n gebeurtenis is net 'n soort van LID (soos velde, eienskappe, ens). En, net soos enige ander soort van lid 'n gebeurtenis het ook 'n tipe. Tog, in die geval van 'n gebeurtenis, die tipe van die gebeurtenis moet gespesifiseer deur 'n afgevaardigde. Byvoorbeeld, kan jy nie verklaar 'n geval van 'n tipe gedefinieer deur 'n koppelvlak.

Slot, kan ons die volgende Waarneming maak: die tipe van 'n gebeurtenis moet gedefinieer word deur 'n afgevaardigde . Dit is die belangrikste verband tussen 'n gebeurtenis en 'n afgevaardigde en word beskryf in die artikel II.18 Definiëring gebeure van ECMA-335 (CLI) Partitions ek aan VI :

  

In tipiese gebruik, die TypeSpec (indien teenwoordig) identifiseer 'n afgevaardigde wie se handtekening ooreenstem met die argumente geslaag om vuur die gebeurtenis se metode.

Maar hierdie feit impliseer NIE dat 'n gebeurtenis gebruik 'n agtergrond afgevaardigde veld . In die waarheid, kan 'n gebeurtenis 'n agtergrond in die veld gebruik van enige ander data struktuur tipe van jou keuse. As jy 'n gebeurtenis uitdruklik te implementeer in C #, jy is vry om die manier waarop jy die event handlers (let wel dat event handlers stoor kies is gevalle van die tipe van die gebeurtenis , wat op sy beurt is mandatorily n tipe afgevaardigde --- van die vorige Waarneming ). Maar, kan jy dié event handlers (wat afgevaardigde gevalle is) in 'n datastruktuur slaan soos 'n List of 'n Dictionary of enige ander anders, of selfs in 'n agtergrond afgevaardigde veld. Maar moenie vergeet dat dit is nie verpligtend dat jy 'n afgevaardigde veld gebruik.

'n gebeurtenis in NET is 'n aangewese kombinasie van 'n metode Voeg en 'n Verwyder metode, wat albei verwag 'n paar spesifieke tipe afgevaardigde. Beide C # en vb.net kan outomaties genereer kode vir die Voeg by en verwyder metodes wat 'n afgevaardigde sal definieer om die geleentheid by inskrywing te hou, en voeg / verwyder die geslaag in delegagte na / van daardie inskrywing afgevaardigde. VB.net sal ook outomaties genereer kode (met die RaiseEvent verklaring) om die inskrywing lys te roep as en slegs as dit is nie leeg; vir een of ander rede, nie C # nie laasgenoemde te genereer.

Let daarop dat terwyl dit algemeen om gebeurtenis inskrywing met behulp van 'n multicast afgevaardigde te bestuur, dit is nie die enigste manier om dit te doen. Van 'n openbare perspektief, 'n aspirant-gebeurtenis intekenaar moet weet hoe om jou te laat 'n voorwerp weet dit wil gebeure ontvang, maar dit hoef nie te weet wat meganisme die uitgewer sal gebruik om die gebeure in te samel. Let ook op dat terwyl elkeen gedefinieer geval datastruktuur in NET blykbaar gedink daar moet 'n openbare middel van die verhoging van hulle, nie C # of vb.net maak gebruik van daardie funksie.

Om te definieer oor die gebeurtenis in'n eenvoudige manier:

Geleentheid is'n VERWYSING om'n afgevaardigde met twee beperkings

  1. Kan nie opgeroep word direk
  2. Kan nie toegeken word waardes direk (e.g eventObj = delegateMethod)

Bogenoemde twee is die swak punte vir die afgevaardigdes en dit is gerig in'n gebeurtenis.Voltooi die kode in: voorbeeld om te wys die verskil in fiddler is hier https://dotnetfiddle.net/5iR3fB .

Skakel die kommentaar tussen die Gebeurtenis en Afgevaardigde en kliënt kode wat hom/ken waardes te delegeer om te verstaan die verskil

Hier is die inline-kode.

 /*
This is working program in Visual Studio.  It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
        Event is an delegate reference with two restrictions for increased protection

            1. Cannot be invoked directly
            2. Cannot assign value to delegate reference directly

Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/

public class RoomTemperatureController
{
    private int _roomTemperature = 25;//Default/Starting room Temperature
    private bool _isAirConditionTurnedOn = false;//Default AC is Off
    private bool _isHeatTurnedOn = false;//Default Heat is Off
    private bool _tempSimulator = false;
    public  delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
    // public  OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 
    public  event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 

    public RoomTemperatureController()
    {
        WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
    }
    private void InternalRoomTemperatuerHandler(int roomTemp)
    {
        System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
    }

    //User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
    public bool TurnRoomTeperatureSimulator
    {
        set
        {
            _tempSimulator = value;
            if (value)
            {
                SimulateRoomTemperature(); //Turn on Simulator              
            }
        }
        get { return _tempSimulator; }
    }
    public void TurnAirCondition(bool val)
    {
        _isAirConditionTurnedOn = val;
        _isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }
    public void TurnHeat(bool val)
    {
        _isHeatTurnedOn = val;
        _isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }

    public async void SimulateRoomTemperature()
    {
        while (_tempSimulator)
        {
            if (_isAirConditionTurnedOn)
                _roomTemperature--;//Decrease Room Temperature if AC is turned On
            if (_isHeatTurnedOn)
                _roomTemperature++;//Decrease Room Temperature if AC is turned On
            System.Console.WriteLine("Temperature :" + _roomTemperature);
            if (WhenRoomTemperatureChange != null)
                WhenRoomTemperatureChange(_roomTemperature);
            System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
        }
    }

}

public class MySweetHome
{
    RoomTemperatureController roomController = null;
    public MySweetHome()
    {
        roomController = new RoomTemperatureController();
        roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
        //roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
        //roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
        roomController.SimulateRoomTemperature();
        System.Threading.Thread.Sleep(5000);
        roomController.TurnAirCondition (true);
        roomController.TurnRoomTeperatureSimulator = true;

    }
    public void TurnHeatOrACBasedOnTemp(int temp)
    {
        if (temp >= 30)
            roomController.TurnAirCondition(true);
        if (temp <= 15)
            roomController.TurnHeat(true);

    }
    public static void Main(string []args)
    {
        MySweetHome home = new MySweetHome();
    }


}

Covariance en Contravariance bied ekstra buigsaamheid om die afgevaardigde voorwerpe. Aan die ander kant, 'n gebeurtenis het geen sodanige konsepte.

  • Covariance kan jy 'n metode te ken aan die afgevaardigde waar die terugkeer tipe van die metode is 'n klas wat afgelei is van die klas wat spesifiseer die terugkeer tipe van die afgevaardigde.
  • Contravariance kan jy 'n metode te ken aan die afgevaardigde waar die parameter tipe van die metode is 'n basis klas van die klas wat gespesifiseerde as die parameter van die afgevaardigde.

Delegeer is 'n tipe-veilige funksie wyser. Gebeurtenis is 'n implementering van die uitgewer-intekenaar ontwerp patroon met behulp van delegeer.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top