switch Statement und Strings



  • Hallo!

    Ich muss einen String nach verschiedenen Inhalten überprüfen und dann, entsprechent dem Inhalt bestimmte Schritte unternehmen. Das mache ich klarerweise mit einer switch - case Struktur:

    switch (meinString) {
        case "eins": MachWas1();
            break;
        case "zwei": MachWas2();
            break;
        case "drei": MachWas3();
            break;
        // usw...
    }
    

    Da es aber recht viele Möglichkeiten für den Inhalt des Strings gibt, frage ich mich ob es Sinn macht die Möglichkeiten, die vermutlich öfter zutreffen an den Anfang des Statements zu packen um die Anzahl an internen String Vergleichen zu minimieren.

    Danke schon mal im Voraus!



  • Jup, ein Blick in den CIL-Code bringt Gewissheit.

    IL_000c:  ldstr      "slidhf"
      IL_0011:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                     string)
      IL_0016:  brtrue.s   IL_0034
    

    Sieht mir so aus, als würde der String mit dem op== verglichen werden und anschließend für true ein branch ansteht. Wahrscheinlich würde eine if-Kaskade den selben Code generieren.



  • Danke für den Hinweis.
    Ich habe mir gerade den IL code von einem if Konstrukt angesehen - sieht eigentlich recht ähnlich aus.
    Na ja ich werde mal sehen welche von meinen möglichen String-Inhalten am öftesten vorkommen und die dann ganz nach oben packen.



  • Hashmap?



  • hasher schrieb:

    Hashmap?

    Das wäre vielleicht auch eine Lösung. Generiere aus dem String eine Hash-Zahl und führe damit die switch-Anweisung durch. Damit erspart man sich "komplexe" String-Vergleiche. Wie viele solcher case-Anweisungen sollen es denn werden?



  • Im Moment sind es 6 Cases.
    Die Frage ist, ob es wirklich schneller ist, wenn ich den String, der verglichen werden soll, hashen muss.



  • Wenn du ihn nur für den Vergleich hasht, bringt es nichts. Die Frage ist doch, ob es überhaupt sinnvoll ist, an einer kritischen Stellen 100mal Strings zu vergleichen. Sind es immer die selben Strings? Wenn nicht, wie kommst du ständig zu 98373 zu vergleichenden Strings?



  • Der Hintergrund sieht so aus:
    Ich lese eine XML Datei mit einem XmlTextReader und da muss ich selbstverständlich wenn NodeType == NodeType.Element den Inhalt von LocalName überprüfen um zu wissen in welche Variable ich den Inhalt des Knotens lesen muss.



  • Vielleicht machst du dir zu viel Aufwand. Schau dir doch mal am besten den XmlSerializer sein, der könnte genau das sein, was du suchst. Und weil .Net von Anfang an ohne Generics rausgekommen ist, solltest du dir einen typsicheren Wrapper bauen:

    private sealed class XmlSerializer<T> {
    			public void Serialize(TextWriter writer, T obj) {
    				encapsulated.Serialize(writer, obj);
    			}
    
                            // Hier noch weitere Overloads für Stream, etc. falls du es brauchst.
    			public T Deserialize(TextReader reader) {
    				return (T)encapsulated.Deserialize(reader);
    			}
    
    			private readonly System.Xml.Serialization.XmlSerializer encapsulated =
    				new System.Xml.Serialization.XmlSerializer(typeof(T));
    		}
    	}
    


  • Verwendet nicht der XmlSerializer intern ein XmlDocument oder eine ähnliche DOM Struktur, die das ganze langsamer und Speicherintensiver als ein paar String Vergleiche macht?



  • IMHO gibt es für ein dickes DOM-Dokument keinen Grund, weil die Eigenschaften nur der Reihe nach gelesen oder geschrieben werden müssen. Wie es wirklich implementiert ist, weiß ich natürlich nicht. Wenn du aber irgendwas mit I/O machst, kannst du davon ausgehen, dass die Serialisierung selber nur einen Bruchteil der Gesamtzeit ausmacht, auf die Platte was zu schreiben ist schon gute-Nacht-verdächtig.
    Und dein XmlTextReader ist auch nicht geschenkt. Tatsächlich wird der Serializer den wohl intern benutzen, denn er hat den Overload Serialize(XmlReader, Object)
    und ich vermute, dass Serialize(TextReader, Object) einen XmlReader mit dem TextReader erstellt und dass Serialize(Stream, Object) einen TextReader aus dem Stream und daraus einen XmlTextReader erstellt.
    Wie auch immer, ich würde den nehmen. Du hast keine Probleme, wenn du nachher die Datenstruktur veränderst, du baust keine Fehler in dein XML ein, es ist einfach die perfekte Lösung und bestimmt nicht die langsamste denkbare.

    Ich hab heute zufällig eh noch mal meinen Wrapper ergänzt, vielleicht willst ihn gleich hernehmen, falls du .Net 2.0 benutzt. In der Doku zum System.Xml.Serialization.XmlSerializer kannst du nachsehen, was dein Typ können muss, damit du ihn so serialisieren kannst.

    using System.IO;
    
    namespace util {
    	/// <summary>Serializes and deserializes objects into and from XML documents. This
    	/// class encapsulates a <c>System.Xml.Serialization.XmlSerializer</c> and provides
    	/// type-safe serialization and deserialization services.</summary>
    	/// <typeparam name="T">The type of the objects to serialize or deserialize.
    	/// </typeparam>
    	public sealed class XmlSerializer<T> {
    		/// <summary>Serializes the specified object.</summary>
    		/// <param name="stream">The <c>System.IO.Stream</c> where to write the resulting
    		/// XML document.</param>
    		/// <param name="obj">The object to serialize.</param>
    		public void Serialize(Stream stream, T obj) {
    			serializer.Serialize(stream, obj);
    		}
    
    		/// <summary>Serializes the specified object.</summary>
    		/// <param name="writer">The <c>System.IO.TextWriter</c> where to write the
    		/// resulting XML document.</param>
    		/// <param name="obj">The object to serialize.</param>
    		public void Serialize(TextWriter writer, T obj) {
    			serializer.Serialize(writer, obj);
    		}
    
    		/// <summary>Serializes the specified object.</summary>
    		/// <param name="writer">The <c>System.Xml.XmlWriter</c> where to write the
    		/// resulting XML document.</param>
    		/// <param name="obj">The object to serialize.</param>
    		public void Serialize(System.Xml.XmlWriter writer, T obj) {
    			serializer.Serialize(writer, obj);
    		}
    
    		/// <summary>Deserializes an XML document to restore a serialized object.
    		/// </summary>
    		/// <param name="stream">The <c>System.IO.Stream</c> that contains the XML
    		/// document to deserialize.</param>
    		/// <returns>The object being deserialized.</returns>
    		public T Deserialize(Stream stream) {
    			return (T)serializer.Deserialize(stream);
    		}
    
    		/// <summary>Deserializes an XML document to restore a serialized object.
    		/// </summary>
    		/// <param name="reader">The <c>System.IO.TextReader</c> that contains the XML
    		/// document to deserialize.</param>
    		/// <returns>The object being deserialized.</returns>
    		public T Deserialize(TextReader reader) {
    			return (T)serializer.Deserialize(reader);
    		}
    
    		/// <summary>Deserializes an XML document to restore a serialized object.
    		/// </summary>
    		/// <param name="reader">The <c>System.Xml.XmlReader</c> that contains the XML
    		/// document to deserialize.</param>
    		/// <returns>The object being deserialized.</returns>
    		public T Deserialize(System.Xml.XmlReader reader) {
    			return (T)serializer.Deserialize(reader);
    		}
    
    		/// <summary>The encapsulated <c>System.Xml.Serialization.XmlSerializer</c> which
    		/// is used to create the XML document.</summary>
    		private readonly System.Xml.Serialization.XmlSerializer serializer =
    				new System.Xml.Serialization.XmlSerializer(typeof(T));
    	}
    }
    


  • Danke für die Klasse, sie funktioniert soweit gut, jedoch bekomme ich eine Exception wegen einer Collection (ich werde mal bei MSDN nachlesen zu Serialization und Collections).

    Dennoch glaube ich, dass eine selbstimplementierte Serialization warscheinlich schneller beim lesen ist als der XmlSerializer. Irgendwo muss der ja auch String Vergleiche haben um zu wissen wo er den Wert hineinlesen muss.



  • Mit Sicherheit, das erklärt aber noch nicht, warum das selbstgemachte schneller sein soll. Und wie gesagt, wenn du File I/O hast, fällt das nicht ins Gewicht. Etwas in eine Datei zu schreiben, dauert Millisekunden, das sind ganz andere Größenordnungen als ein paar Mikrosekunden für den String-Vergleich.



  • Das stimmt. Eigentlich müssten die manuelle und die XmlSerializer Variante in etwa gleich schnell sein.

    Es geht bei der ganzen Sache um das Einlesen der Albumdateien meines Fotoalbums. Die Dateien sind so aufgebaut.

    <?xml version="1.0" encoding="utf-8"?>
    <VPO_Album>
    <!-- Albumheader -->
      <AlbumName>MyAlbum</AlbumName>
      <AlbumVersion>1.3</AlbumVersion>
      <SubAlbCount>2</SubAlbCount>
      <PhotosCount>3</PhotosCount>
      <SubAlbum>..\NochEinAlbum.vpoa</SubAlbum>
    <!-- weitere Unteralben -->
    
    <!-- Fotos Collection -->
      <Photo>
        <Path>untitled2.bmp</Path>
        <Thumbnail>Thumbnails\th_untitled2.bmp</Thumbnail>
        <Title>untitled2.bmp</Title>
        <Description />
        <TimeTaken>8/7/2005 10:44:25 PM</TimeTaken>
      </Photo>
    <!-- Weitere Fotos -->
    </VPO_Album>
    

    Ich lese beim Start des Albums nur den Albumheader und erst wenn der Benutzer ein Album öffnet werden die Fotos gelesen. Das Problem mit den Stringvergleichen ist, dass beim Albumheader auf 6 verschiedene Knotennamen und bei jedem Foto auf 5 verschiedene Knotennamen prüfen muss. Ich glaube der XmlSerializer wäre da auch nicht besser. Außerdem muss ich das ganze ja in zwei Schritten laden. Gibt's damit überhaupt die Möglichkeit den XmlSerializer zu verwenden?



  • Du brauchst natürlich für alles, was du einzeln auslesen willst, eine einzelne Klasse/Struktur, weil der Serializer immer ein ganzes Objekt deserialisiert. Du kannst auch Kompositionen haben, wie

    class Album {
        Header header;
        PhotoCollection photos;
    }
    

    und beim Serialisieren macht er dir dann die richtige Baumstruktur. Um dann nur den Header auszulesen, musst du im Stream vorspringen, was vielleicht nicht trivial ist. Obwohl, mit dem XmlReader schaun, wann der Header anfängt und ab da deserialisieren geht wohl. Wenn dein Album klein ist, bringt dir das aber nichts. Ob du von der Platte 100 Byte oder 100 Kilobyte holst, dürfte keinen Unterschied machen und das Parsen des XML verschwindet zeitlich sowieso, wenn du auf das Dateisystem zugreifst.



  • Die Idee ist gar nicht schlecht. Ich muss nur mal sehen wie ich die Transition schaffe, da es bereits ein besthendes Albumformat gibt.
    Danke für alles!


Anmelden zum Antworten