Suche: ARP

Objekte mit C# dynamisch erzeugen 👍 👎

Insbesondere bei der Entwicklung von unterstützenden Tools kann es hilfreich sein, Objekte dynamisch erzeugen zu können, ohne vorher den konkreten Typ zu kennen. Ich habe so etwas beispielsweise einmal im Rahmen einer interaktiven API-Dokumentation genutzt. Das .NET-Framework bietet hierfür die Activator-Klasse.

Als Basis für das Beispiel soll folgende triviale Klasse dienen:
Beispielklasse
01020304050607080910111213141516
public class Person {    public string FirstName {        get; set;    }
public string LastName { get; set; }
public Person() {}
public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; }}
Hiervon können wir nun dynamisch ein Objekt erzeugen:
Objekt dynamisch erzeugen
01020304
Activator.CreateInstance(Type.GetType("Person"), new[] {    "Holger",    "Stehle"});
Dieses Vorgehen ist jedoch nur in Ausnahmefällen zu empfehlen, da ihr schließlich die Vorteile der statischen Typisierung größtenteils verliert. Es bietet sich daher je nach Anwendungsfall die Verwendung von Schnittstellen (z. B. bei Plugins) oder die Verwendung generischer Programmierung (z. B. zur internen Abstraktion) an.

Die in diesem Beitrag vorgestellte Klasse bietet auch eine Variante mit Typparameter an:
Objekt per Typparameter erzeugen
01
Person person = Activator.CreateInstance<Person>();
Diese Funktionalität ist in der Praxis zur "normalen" Entwicklung jedoch nur bedingt hilfreich, schließlich musste dazu der Typ bereits vorab bekannt sein und ihr hättet das Objekt auch einfach per new erzeugen können.

Mit generischer Programmierung zeigt sich ein weiteres Einsatzfeld, welches sich jedoch mit einer entsprechenden Einschränkung des Typparameters auch anderweitig lösen lässt und für mehr Unterstützung des Typsystems sorgt, da ihr für eure Zwecke weitere Einschränkungen (wie beispielsweise Schnittstellen) angeben könnt:
Objekt per Typparameter-Einschränkung erzeugen
01020304050607
public static class ObjectUtility {    public static T CreateInstance<T>() where T : new() {        return new T();    }}
Person person = ObjectUtility.CreateInstance<Person>();

Temporäre Datei mit C# erstellen und implizit löschen 👍 👎

Hin und wieder kann es – beispielsweise bei der Arbeit mit Datenströmen – sinnvoll bzw. notwendig sein, mit Dateien zu arbeiten, die nur vorübergehend benötigt werden. Das .NET-Framework unterstützt dies mit der Methode Path.GetTempFileName(), ohne jedoch die Datei implizit wieder zu entfernen. Um diesen kleinen Makel zu beheben, bietet sich eine kompakte Implementierung wie die folgende Lösung an, welche auf using basiert:
Klasse implementieren
010203040506070809101112
public class TemporaryFile : IDisposable {    public string Name {        get;        private set;    }
public TemporaryFile() { this.Name = Path.GetTempFileName(); }
public void Dispose() => File.Delete(this.Name);}
Die Verwendung gestaltet sich nun sehr komfortabel, wobei die Datei innerhalb der entsprechenden Anweisung existiert und wie üblich verwendet werden kann; anschließend wird diese automatisch wieder gelöscht:
Klasse verwenden
01020304050607080910111213
/* Datei existiert noch nicht */
using(TemporaryFile temporaryFile = new TemporaryFile()) { /* Datei existiert ab sofort */
File.WriteAllBytes(temporaryFile.Name, new byte[] { 0x01, 0x03, 0x03, 0x07 });
/* Datei existiert weiterhin */}
/* Datei existiert nicht mehr */

PHP-Funktionen in C# (noch nicht) als Programmbibliothek 👍 👎

Meine beispielhaften PHP-Funktionen in C# erfreuen sich gewisser Beliebtheit, sodass mich mittlerweile schon ein paar mal die Frage erreicht hat, ob ich diese nicht als Programmbibliothek zur Verfügung stellen möchte.

Aus technischer Sicht wäre das tatsächlich kein Problem und grundsätzlich sogar mit überschaubarem Aufwand automatisierbar. Ich habe daher auch selbst schon einmal mit dem Gedanken der Veröffentlichung als Paket gespielt, dennoch möchte ich erst einmal Abstand davon nehmen und zwar vor allem aus folgenden Gründen:
  • Design

    Die Umsetzung der Methoden (Bezeichner, Datentypen, Parameter) ist stark an PHP angelehnt, sprich ist soweit möglich identisch zu den gleichnamigen PHP-Funktionen. Diese Umsetzung widerspricht jedoch in nahezu allen Fällen in vielerlei Hinsicht den .NET-Guidelines für Programmbibliotheken und auch die Datentypen entsprechen oftmals nicht den bei C# für den jeweiligen Einsatzzweck üblichen.

  • Qualität

    Die Umsetzungen sollen einen Ansatz bzw. Tipps liefern, entsprechen jedoch in einigen Fällen nicht einer Qualität, die einen sofortigen und ungeprüften Einsatz in Produktionsumgebungen ermöglicht. Dies wäre für eine tatsächliche Veröffentlichung jedoch zwingend erforderlich. Darüber hinaus wären an einigen Stellen Optimierungen für den effizienten Einsatz in echten Projekten durchaus sinnvoll.

Dennoch stimme ich natürlich grundsätzlich zu, dass die Implementierungen einige praktische Funktionen anbieten, die in PHP durchaus üblich sind, in C# standardmäßig jedoch nicht ohne Weiteres zur Verfügung stehen. Daher werde ich an einer entsprechenden Programmbibliothek arbeiten, die ausschließlich solche Funktionalität in stabiler Qualität zur Verfügung stellt. Einen konkreten Zeitplan kann und möchte ich derzeit jedoch noch nicht nennen.

Melodien mit Systemtönen in C# 👍 👎

Im letzten Beitrag hatte ich Systemtöne mit C# vorgestellt. Der Übersicht wegen – und nicht ganz ernstzunehmen – möchte ich mit diesem separaten Beitrag noch ein paar Beispiele zur Umsetzung einfacher Melodien zeigen.

Dazu implementieren wir zuerst eine kleine Hilfsklasse mit einigen vordefinierten Werten:
Klasse zur Arbeit mit der Tonart C-Dur
01020304050607080910111213141516171819
public static class BeepMusic {    private static readonly Dictionary<char, int> frequencyCollection = new Dictionary<char, int>() {        ['C'] = 264, ['D'] = 297, ['E'] = 330, ['F'] = 352,        ['G'] = 396, ['A'] = 440, ['H'] = 495, ['c'] = 528    };
public static void PlayNotes(Duration duration, string noteList) { foreach(char note in noteList) { Console.Beep(frequencyCollection[note], (int) duration); } }
public enum Duration { Whole = 1600, Half = (Whole / 2), Quarter = (Half / 2), Quaver = (Quarter / 2) }}
Nun können wir diese Klasse statisch importieren und (sehr) einfache Melodien erzeugen:
Melodien
01020304050607080910111213141516171819202122232425262728293031323334353637383940
using static BeepMusic;
// TonleiterPlayNotes(Duration.Quarter, "CDEFGAHc");
// Alle meine EntchenPlayNotes(Duration.Quaver, "CDEF");PlayNotes(Duration.Quarter, "GG");PlayNotes(Duration.Quaver, "AAAA");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quaver, "AAAA");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quaver, "FFFF");PlayNotes(Duration.Quarter, "EE");PlayNotes(Duration.Quaver, "GGGG");PlayNotes(Duration.Half, "C");
// Hänschen KleinPlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CDEFGG");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CEGG");PlayNotes(Duration.Whole, "C");PlayNotes(Duration.Quarter, "DDDDDE");PlayNotes(Duration.Half, "F");PlayNotes(Duration.Quarter, "EEEEEF");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CEGG");PlayNotes(Duration.Whole, "C");
Interessante Effekte ergeben sich darüber hinaus bei (pseudo-)zufälligen Tonfolgen. In einem späteren Beitrag möchte ich gerne aufzeigen, wie man auch etwas ernsthafter mit diesem Thema in C# umgehen kann.

ZIP-Archive mit C# (ent-)packen 👍 👎

In einem früheren Beitrag zur (De-)Kompression mit C# hatte ich bereits einen Beitrag zu ZIP-Archiven in Aussicht gestellt. Diese sind praktisch, um Dateien und Verzeichnisstrukturen komprimiert und zusammenhängend speichern und austauschen zu können und werden außerdem auf praktisch jeder Plattform unterstützt. Daher möchte ich mit diesem Beitrag zeigen, wie sich mit C# entsprechende Archive automatisch erstellen lassen.

Das .NET-Framework liefert im Namensraum System.IO.Compression grundlegende Funktionalität zur Arbeit mit entsprechenden Archiven, auf die wir in diesem Beitrag setzen werden. Darüber hinaus gibt es jedoch eine Vielzahl weiterer Implementierungen (z. B. DotNetZip oder SharpZipLib), welche beispielsweise die Erstellung von passwortgeschützten Archiven ermöglichen und weitere Konfigurationsmöglichkeiten bieten.

Zuerst möchte ich ein paar Möglichkeiten aufzeigen, um ZIP-Archive zu erstellen:
Archiv aus bestehendem Verzeichnis erstellen
01
ZipFile.CreateFromDirectory("data", "data.zip");
Archiv aus bestehenden Dateien erstellen
010203040506
using(FileStream fileStream = File.OpenWrite("data.zip")) {    using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create)) {        zipArchive.CreateEntryFromFile("data/a.txt", "a.txt");        zipArchive.CreateEntryFromFile("data/b.txt", "b.txt");    }}
Archiv manuell zusammenstellen
010203040506070809
using(FileStream fileStream = File.OpenWrite("data.zip")) {    using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create)) {        ZipArchiveEntry zipArchiveEntry = zipArchive.CreateEntry("README");
using(StreamWriter streamWriter = new StreamWriter(zipArchiveEntry.Open())) { streamWriter.Write("Hallo Welt!"); } }}
Abschließend möchten wir auf bestehende Archive zugreifen:
Archiv vollständig entpacken
01
ZipFile.ExtractToDirectory("data.zip", "data");
Archiv einlesen und verarbeiten
01020304050607080910111213141516
using(FileStream fileStream = File.OpenRead("data.zip")) {    using(ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) {        foreach(ZipArchiveEntry zipArchiveEntry in zipArchive.Entries) {              // "README" separat berücksichtigen            if(zipArchiveEntry.Name == "README") {                using(StreamReader streamReader = new StreamReader(zipArchiveEntry.Open())) {                      // Inhalt einlesen und auf Konsole ausgeben                    Console.WriteLine(streamReader.ReadToEnd());                }            } else {                  // Eintrag als Datei speichern                zipArchiveEntry.ExtractToFile(zipArchiveEntry.Name);            }        }    }}
Sehr viel mehr Möglichkeiten stehen leider tatsächlich nicht zur Verfügung; für weitergehende Einstellungen gibt es jedoch beispielsweise die bereits erwähnten externen Bibliotheken. Durch die Verwendung von Datenströmen besteht aber grundsätzlich die Möglichkeit, nahezu beliebige Quellen (und Ziele) für Archiv-Einträge heranzuziehen.

123

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche