Laden...

Events vs. Interfaces?

Erstellt von gelöschtem Konto vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.622 Views
Gelöschter Account
vor 13 Jahren
Events vs. Interfaces?

Kürzlich gab es eine kleine Diskussion in einem anderen Forum, da habe ich den Source von einem meienr Projekte zur Verfügung gestellt.
In dem Projekt benutze ich Events.

Durfte ich mir doch gleich anhören, wie unperformant Events doch sind, und man doch lieber alles mit Interfaces lösen solle.

Ich bin da jetzt nicht sooo dermaßen bewandert um den riesigen Performanceunterschied zu erkennen und frage deshalb hier nach, weil ich weiß das hier die ganzen Cracks rumrennen.

Also ist es wirklich so schlimm, Events zu benutzen? Wo genau besteht der Unterschied? Muss ich mich jetzt schämen weil ich Events benutze?

731 Beiträge seit 2006
vor 13 Jahren

Ich kann jetzt nicht den logischen Zusammenhang zwischen Interfaces und Events sehen 😄

1.044 Beiträge seit 2008
vor 13 Jahren

Hallo DNAofDeath,

Events kannst du nicht mit Interfaces vergleichen! Den Unterschied verstehe ich auch nicht, die anderen werden dich wohl auch nicht verstehen. Kannst du uns das ein wenig genauer erläutern?

zero_x

799 Beiträge seit 2007
vor 13 Jahren

Ich denke du sprichst von der Delegate-Definition aller Java bei der man mit Interfaces um das Fehlen von Delegates herumschifft und daher das ganze auch für das Event-Pattern verwendet.

Dazu kann ich nur eins sagen: In C# nie - außer es gibt zwingende Gründe wie eine Waffe am Kopf - Features die in der Sprache implementiert sind durch "kluge" Performance steigernde und selbst zusammen gehackte Maßnahmen ersetzen.

Meiner Erfahrung nach, sind in Projekten die mit C# umgesetzt sind die Performance kritischen Teilen höchstens der Code der in den jeweiligen Methoden ausgeführt wird. Und dann ist es egal ob dieser über Events gefeuert oder über Methoden in den von den Interfaces abgeleiteten Klassen ausgeführt wird.

Oder mit den Worten eines weisen Meisters:

Premature optimization is the root of all evil (or at least most of it) in programming.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
Gelöschter Account
vor 13 Jahren

Mir wurden folgende Links zum 'am besten auswendig lernen' gegeben:
How to Write High-Performance C# Code
Weak Events in C#

Keine ahnung was ich damit anfangen soll... habs mal angelesen, aber das hat irgendwie für mich nix mehr mit Events zu tun...

Der andere meinte auch noch

Eigene Worte: Events sind langsam.

Falls dir das nicht reicht, les dir die Artikel durch. Falls du nicht lesen kannst/willst, frag nicht nach. Ich werde dir wohl kaum den verlinkten Artikel und die Grundstrukturen und Funktionsweisen von Events in ein paar Worten wiedergeben können, das ist Irrsinn. Du musst den Unterschied zwischen Events und Interfaces kennen lernen (auch verstehen!) und vor allem wie diese intern gehandelt werden.

799 Beiträge seit 2007
vor 13 Jahren

IMHO ein Irrweg Events zu verteufeln.

Und die Argumentation Events führen in den meisten Fällen zu schlechteren Designs kann ich auch nicht nachvollziehen.

Falls du nicht lesen kannst/willst, frag nicht nach. Ich werde dir wohl kaum den verlinkten Artikel und die Grundstrukturen und Funktionsweisen von Events in ein paar Worten wiedergeben können, das ist Irrsinn.

Solche präpotenten Antworten würden mich auch dazu verleiten das Forum zu meiden. Vor allem, da ich es unprofessionell finde, im System integrierte Funktionalität künstlich gegen eigene Sachen auszutauschen. Das macht den Code nur undurchsichtig ohne richtigen Vorteil. (Die Geschwindigkeit bei Events halte ich in den meisten Fällen tatsächlich für vernachlässigbar. Da würde ich vorher bei der asynchronen Ausführung der jeweiligen EventHandler in den Events ansetzen, denn das bringt wirklich was.)

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
C
2.121 Beiträge seit 2010
vor 13 Jahren

Durfte ich mir doch gleich anhören, wie unperformant Events doch sind, und man doch lieber alles mit Interfaces lösen solle.

Da würde sich bei mir die Überlegung starten, wie ich den Satz "du Depp, wenn du keine Ahnung hast wovon du redest, halt dich doch einfach raus" so formuliere, dass es höflich bleibt und dem anderen trotzdem unmissverständlich klar wird, was für einen Mist er da redet.
Du hast übrigens die Erlaubnis diesen Satz ins andere Forum zu kopieren 😃

Gelöschter Account
vor 13 Jahren

Also ich hab jetzt mal das Projekt in den Anhang gesetzt, ich bin mir aber immernoch nicht darüber im Klaren, ob und wo es jetzt wirkliche Alternativen zu Events gibt 0o.
Ich meine irgendwoher muss der enorme Groll meines Mitschreibers doch kommen.

Wenn Jemand zuviel Zeit oder gesteigertes Interesse hat, kann er ja ein kleines Code-Review machen und mir sagen was ich alles falsch gemacht habe in meinem kleinen Projekt.

Bezüglich der Performance sehe ich bis jetzt keine Probleme, ich nutze dieses Konzept so auch in einem Chat den ich programmiert habe, und wie gesagt, er läuft.

799 Beiträge seit 2007
vor 13 Jahren

Psychologisch gesehen gibt es genug Gründe warum Leute glauben sie müssten Trollen. Ich persönlich würde mir aber keine großen Gedanken über diesen Troll machen.

Technisch bietet die Implementation vernachlässigbar kleine Vorteile. Vom Entwicklerstandpunkt ist das ganze von Nachteil da man eben Dinge anders implementiert als es die meisten Programmierer erwarten bzw. Code einführt der auch noch extra eine Fehlerquelle sein kann u. getestet werden muss.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

über die Performance bzgl. Event oder nicht brauchst du dir keine Kopf machen*. Setzt Events dort ein wo sie sinnvoll sind und setzt Interfaces dort ein wo sie sinnvoll sind.

ZB ist das klassische Observer-Pattern mit Interfaces implementiert. In .net kann das aber eleganter mit Events implementiert werden. Beim klassischen standen einfach keine Events zur Verfügung.

Worauf du tatsächlich hinauswillst weiß ich nicht. Bedenke dass Events auf Delegaten basieren. Interfaces hingegen sind stellen einen Vertrag dar den Typen welche das Interface implementieren erfüllen müssen.
Vllt. klärt das ja schon einiges.

* zumal der Unterschied zwischen Delegate-Call und Interface-Call nahezu ident sind. Das im oben verlinkten Artikel stimmt nicht mehr, denn das wurde in .net überarbeitet. Ganz zu Beginn war es so wie im Artikel erwähnt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Gelöschter Account
vor 13 Jahren

Psychologisch gesehen gibt es genug Gründe warum Leute glauben sie müssten Trollen. Ich persönlich würde mir aber keine großen Gedanken über diesen Troll machen.

Technisch bietet die Implementation vernachlässigbar kleine Vorteile. Vom Entwicklerstandpunkt ist das ganze von Nachteil da man eben Dinge anders implementiert als es die meisten Programmierer erwarten bzw. Code einführt der auch noch extra eine Fehlerquelle sein kann u. getestet werden muss.

Der Troll ist Technischer Administrator von dem Forum und Fachinformatiker für Anwendungsentwicklung... und zwar bis auf mich der einzige in dem Forum.

Ich weiß nicht was ich sagen soll? Garnix mehr? Dann wähnt er sich im Recht...

Hinweis von gfoidl vor 13 Jahren

Bitte hier keine Probleme mit/über externen Foren-Mitgliedern diskutieren. Bleibt bei der Sache Events vs. Interfaces. Der andere kann ja nicht mal Stellung beziehen.

Alles sachliche wurde (mehr oder weniger) bereits im Thema behandelt.

D
38 Beiträge seit 2009
vor 13 Jahren

Hallo DNAofDeath,

ich habe die Client.cs und Server.cs im Server überflogen und an Deiner Stelle würde ich mir weniger um die Events, als viel mehr um den Code an sich, Gedanken machen.

  1. Alles kreuz und quer. Keine Regions, private Member mitten zwischen Funktionen definiert usw.

  2. Konstruktoren sind in der Regel nur für Parameterübergaben gedacht. Du startest dort einen asynchronen Vorgang. Das würde ich in eine extra Methode mit aussagekräftigen Namen auslagern.

  3. Öffentliche Eigenschaften und Methoden sind nicht dokumentiert.

  4. Ausnahmen werden in den Funktionen abgefangen und via Messagebox angezeigt. 8o

  5. In den Methoden WaitForData und OnDataReceived wird jede Zeile dokumentiert. Jeder weiß, dass die Methode EndReceive() den Empfangsvorgang beendet. Solltest Du später Änderungen am Code vornehmen, dann stimmen die Kommentare nicht mehr. Lass sie ganz weg.

  6. Übergebene Parameter werden überhaupt nicht geprüft!

  7. Eigenschaften die im Konstruktor übergeben werden, können von außen wieder verändert werden?!

  8. Die Methode WaitForData() wird im Konstruktor gerufen und kann von außen x-mal gerufen werden. Soll eine Klasse einen ClientSocket haben und dann noch verschiedene andere? Glaube ich nicht.

  9. Du beginnst in WaitForData() einen asynchronen Vorgang und merkst Dir nicht das IAsyncResult. In der Regel legt man drei Methoden an. Eine die asynchron startet, eine die auf das Ende dieses Vorganges wartet und eine dritte, welche beide Methoden aufruft, wenn man den ganzen Vorgang synchron haben möchte. Das stellt es dem Aufrufer frei die Funktionalität zu nutzen, wie er es möchte.

  10. Es sind mehrere Klassen in eines CS-Datei.

  11. Die Methode OnDataReceived() ist öffentlich, obwohl sie eigentlich privat sein sollte.

  12. In OnDataReceived prüfst Du die Länge auf zwei, holst dir die Länge deiner Daten und verlässt dann die Methode? Meiner Meinung nach kommst Du nie in den else Zweig, wenn Du das gesamte Paket auf einmal wegschickst. Weiterhin ist mir unklar, warum Du die Verbindung trennst und anschließend wieder neu aufbaust.

  13. Als Encoder nimmst Du default. Da die Nachricht dein eigenes Format besitzt, sollte z.B. UTF8 (je nach Anforderung) festgelegt werden.

  14. Was ist der Unterschied zwischen dem MessageReceived- und MessageSend-Event? Ein Event müsste da eigentlich reichen.

  15. Das führt dazu, dass der Benutzer der Klasse alles mögliche falsch machen kann, was nur geht und niemand weiß, wie das Programm im Fehlerfall reagiert. Bugs sind vorprogrammiert.

Unterm Strich ganz schlechter Stil. X(

Ich habe die Klasse mal fix überarbeitet. Bei mir würde die ungetestet in etwas so aussehen:


    /// <summary>
    /// ToDo: Hier sollte stehen, für was die Klasse verantworlich ist.
    /// </summary>
    public class MessageClient
    {
        #region private Member

        private bool _IsWaiting;
        private Socket _Socket;
        private AsyncCallback _CallBack;

        #endregion

        #region Konstruktor

        /// <summary>
        /// Erstellt eine neue Instanz der <see cref="Client"/>-Klasse.
        /// </summary>
        /// <param name="socket">Der Socket für die Kommunikation.</param>
        /// <exception cref="ArgumentNullException">Wird ausgelöst, wenn der <paramref name="socket"/>-Parameter
        /// <see langword="null"/> ist.</exception>
        public MessageClient(Socket socket)
        {
            if (socket == null)
                throw new ArgumentNullException("socket");

            _Socket = socket;

            _IsWaiting = false;
        }

        #endregion

        #region öffentliche Methoden

        /// <summary>
        /// Beginnt das Warten auf Nachrichten.
        /// </summary>
        /// <exception cref="InvalidOperationException">Wird ausgelöst, wenn bereits auf Daten gewartet wird.</exception>
        public IAsyncResult BeginWaitForMessage()
        {
            if (_IsWaiting)
                throw new InvalidOperationException("Vorgang bereits gestartet");

            _CallBack = new AsyncCallback(OnDataReceived);

            var packet = new SocketPacket(_Socket);

            var result = _Socket.BeginReceive(
                packet.Buffer,
                0,
                packet.Buffer.Length,
                SocketFlags.None,
                _CallBack,
                packet);

            _IsWaiting = true;

            return result;
        }

        /// <summary>
        /// Beendet den noch ausstehenden Vorgang.
        /// </summary>
        /// <param name="result">Das <see cref="IAsyncResult"/> zum Vorgang.</param>
        /// <exception cref="ArgumentNullException">Wird ausgelöst, wenn der <paramref name="result"/>-Parameter
        /// <see langword="null"/> ist.</exception>
        /// <exception cref="InvalidOperationException">Wird ausgelöst, wenn noch nicht auf Daten gewartet wird.</exception>
        public void EndWaitForMessage(IAsyncResult result)
        {
            if (result == null)
                throw new ArgumentNullException("result");

            if (!_IsWaiting)
                throw new InvalidOperationException("Vorgang noch nicht gestartet");

            _Socket.EndReceive(result);

            _IsWaiting = false;
        }

        /// <summary>
        /// Wartet auf Nachrichten.
        /// </summary>
        public void WaitForMessage()
        {
            EndWaitForMessage(BeginWaitForMessage());
        }

        #endregion

        #region öffentliche Events

        /// <summary>
        /// Wird ausgelöst, wenn eine Nachricht empfangen wurde.
        /// </summary>
        public event EventHandler<ReceivedMsgEventArgs> MessageReceived;

        #endregion

        #region private Methoden

        private void OnDataReceived(IAsyncResult asyn)
        {
            var socketData = (SocketPacket)asyn.AsyncState;

            var packetLength = System.BitConverter.ToInt16(socketData.Buffer, 0);
                    
            var message = Encoding.UTF8.GetString(socketData.Buffer, 2, packetLength);

            var messageReceived = MessageReceived;
                                    
            if (messageReceived != null)
                messageReceived(this, new ReceivedMsgEventArgs(message));
        }

        #endregion
    }

Merken Sie was? 🤔

Selbst wenn der Code jetzt sauber ist, bekommst Du ein Problem, wenn die Daten nicht mit ein Mal ankommen. Dann wird als Länge z.B. 1048 Bytes angegeben und es werden nur 1024 übermittelt. Da kommt eine IndexOutOfRangeException. Man müsste dann so lange sammeln, bis alle Daten komplett sind und dann erst das Event losschicken. Du bräuchtest also einen Thread mit einer Queue.

Weiterhin würde ich mir einen richtigen Header für die Nachricht definieren, auf den Aufbau prüfen und anschließend erst verarbeiten. Fehlerhafte Pakete werden dabei verworfen! Wenn man hart auf die ersten zwei Bytes geht, kann das für böse Überraschungen sorgen.

Ich hoffe Du nimmst Dir das zu Herzen ?(

dispose

P.S. Die Verwendung von Events geht meiner Meinung nach bei diesem Anwendungsfall in Ordnung.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo DNAofDeath,

ich sehe gerade am Beitrag von dispose dass du Sockets verwendest. Ganz ehrlich: Da ist jede Überlegung ob der Code durch Events oder Interfaces schneller wird (und hier gehts um Nanosekunden pro Aufruf) absoluter Schwachsinn denn die Socketverbindung ist sowieso eine viel größere Bremse. Wenn es was zu optimieren gäbe dann das, aber nicht Event vs. Interface.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

Gelöschter Account
vor 13 Jahren

Was gibts denn da für Alternativen? Gibts was schnelleres? Besseres? Immer her damit, bin ja lernwillig.

@dispose danke, SO stelle ich mir eine vernünftige Kritik vor, ich werde jeden deiner Punkte nach und nach abarbeiten.
Ich poche nämlich nicht drauf der beste Programmierer der Welt zu sein xD

Nur das hier habe ich nicht verstanden:

Selbst wenn der Code jetzt sauber ist, bekommst Du ein Problem, wenn die Daten nicht mit ein Mal ankommen. Dann wird als Länge z.B. 1048 Bytes angegeben und es werden nur 1024 übermittelt. Da kommt eine IndexOutOfRangeException. Man müsste dann so lange sammeln, bis alle Daten komplett sind und dann erst das Event losschicken. Du bräuchtest also einen Thread mit einer Queue.

Weiterhin würde ich mir einen richtigen Header für die Nachricht definieren, auf den Aufbau prüfen und anschließend erst verarbeiten. Fehlerhafte Pakete werden dabei verworfen! Wenn man hart auf die ersten zwei Bytes geht, kann das für böse Überraschungen sorgen.

eh? was?

//Edit vielleicht sei heir noch zu erwähnen, das das ein Grundgerüst sein soll, und nicht auf Vollständigkeit besteht.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

Was gibts denn da für Alternativen? Gibts was schnelleres? Besseres?

Alternativen: ja wie zB Remoting, WCF, Zyan, etc.
Schnelleres: nicht das ich wüßte
Besseres: kommt auf die Kriterien darauf an

Damit wollte ich nur sagen dass es dort zu optimieren gilt wo das Bottleneck ("die Bremse") ist und nicht sonst irgendwo. Gleiches gilt auch für die Parallelisierung. Paralleler Code kann nie schneller als der Anteil an sequentiellen Code werden - aber ich schweife ab...

Zur Optimierung wurdest du eh schon auf Donald E. Knuth hingewiesen:

Premature optimization is the root of all evil (or at least most of it) in programming.

Beherzige das und erstelle den Code so dass er leserlich ist. Dazu zähle ich auch dass er nach den üblichen OO-Prinzipien erstellt wird.
Im Forum gabs auch schon unzählige Diskussion über Optimierungen -> suche mal danach wenns dich interessiert. Dabei kannst du auch viel lernen. Aber: Beachte Knuth's Aussage.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo,

noch was zu der Überlegung Events vs. Interfaces:
Wenn man mit Interfaces annähernd das nachbauen will, was Events leisten, dann handelt man sich umso größere Abhängigkeiten ein.
Der Auslöser des Events müsste ja alle Subscriber (per Interface) kennen. Da kann dann von Entkopplung keine Rede mehr sein. Oder wie ist das überhaupt gemeint?

Was gibts denn da für Alternativen? Gibts was schnelleres? Besseres?

Nein. Alle Netzwerkverbindungen werden letztlich intern über Sockets realisiert. Und die brauchen eben ihre Zeit.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

D
38 Beiträge seit 2009
vor 13 Jahren

Du gibst bei BeginReceive() ein Byte-Array mit. Dort werden die Daten rein geschrieben. Wer sagt, dass wenn Du 10 MB mit einmal überträgst, alles in dem Array steht? Dann kommt das Stück für Stück an. Dann musst Du das verketten, was ohne Header nicht geht.

Und wenn man es so macht wie oben, dann steht in den ersten zwei Byte die Länge (10 MB), aber es wurden vorerst nur 1 MB übertragen. Dann rufst du GetString() mit Länge 10 MB auf ein 1 MB großes Array.

So lange nur kleine Datenmengen übertragen werden hat man das Problem nicht.

In dem Beispiel oben ist der Buffer aber immer 2 Byte groß, dass müsste noch geändert werden.

dispose

Gelöschter Account
vor 13 Jahren

Eigendlich nicht, denn die Buffergröße ist nur jedes 1. Paket 2byte groß.
Dieses Paket gibt ja die Größe des folgenden Paketes an.
(funktioniert bei mir auch genau so... siehe variable 'nextPacketLength')
Hier gibt es natürlich ein Problem wenn 10MB Daten ankommen.
Ich glaube jedoch zu wissen, das wenn Jemand versucht, in einem Chat wo im Client schon eine Zeichenbegrenzung durch das entsprechende Eingabefeld herrscht, 10MB Daten an meinen Server versucht zu senden, eindeutig ein Versuch vorliegt, meinen Server down zu bekommen.
Du hast natürlich recht mit deinem Paketheader, bei größeren Paketen müsste ich das ganze Splitten und in eine Queue hauen, darüber habe ich mir auch schon Gedanken gemacht, für sowas kann man sich ja ein passendes Protokoll zurechtschneidern, müsste ich sowieso noch, da eine Komprimierung und eine Verschlüsselung geplant sind.

Wie gesagt, das ganze war eher als Grundlage gedacht, damit wers brauchen kann, seinen Krams darauf aufbauen kann, aber das ist wohl so schlecht programmiert, das man es keinem zumuten kann xD daher hab ichs auch rausgenommen aus dem anderen Forum.

Hinweis von gfoidl vor 13 Jahren

Bitte beachte auch [Hinweis] Wie poste ich richtig? Punkt 2.3

D
38 Beiträge seit 2009
vor 13 Jahren

Eigendlich nicht, denn die Buffergröße ist nur jedes 1. Paket 2byte groß.

Und wenn jemand zwei Zeichen als Nachricht sendet? Dann hast Du die ersten zwei Byte als Länge. Anschließend kommen wieder zwei Zeichen als Nutzdaten, welche Du wiederum als Längenangabe via == 2 interpretierst und dann gibt es eine Ausnahme!

Ich habe es übrigens so geändert, dass alles mit einmal gelesen wird.

Ich glaube jedoch zu wissen, das wenn Jemand versucht, in einem Chat wo im Client schon eine Zeichenbegrenzung durch das entsprechende Eingabefeld herrscht, 10MB Daten an meinen Server versucht zu senden, eindeutig ein Versuch vorliegt, meinen Server down zu bekommen.

Glauben, wissen und hoffen. =)

Du entwickelst eine Komponente die nicht wissen soll und gar nicht wissen darf, wer sie wie benutzt. Ziel ist es immer stabile und robuste Komponenten zu entwickeln und nicht alles auf Vermutungen und Wissen über die restlichen Programmteile aufzubauen.

Wenn Dir das mit der Queue und dem Thread zu viel ist, geht es auch einfacher mit privaten Membern und einem StringBuilder (z.B.).

dispose

T
381 Beiträge seit 2009
vor 13 Jahren

Beachte dringen, dass 2 Byte zum Codieren der Zahlen 0 - 65536 ausreichen (signed wirst du noch weniger haben)
1 MB hat 1024 * 1024 = 1048576 bytes und lässt sich damit nicht mit 2 Bytes ankündigen. In die Falle bin ich selbst schon gelaufen.