Suche: API

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>();

RandomNumberGenerator-Implementierung in C# für random.org 👍 👎

In einem früheren Beitrag zum Thema (Pseudo-)Zufallszahlen in C# hatte ich bereits die Klassen Random und RNGCryptoServiceProvider vorgestellt. Letztere leitet dabei von der abstrakten Klasse RandomNumberGenerator ab, welche wir im Folgenden ebenfalls als Basis für eine Klasse verwenden werden.

Unsere Klasse soll die Werte jedoch nicht selbst erzeugen, sondern vom Anbieter random.org über dessen Schnittstelle auf Basis von JSON-RPC beziehen. Der Dienst ist – zumindest derzeit und in gewissen Grenzen – kostenlos, wobei ein API-Schlüssel benötigt wird, den ihr dem Konstruktor der Klasse übergeben müsst.

Hinweis: Ich möchte vor der eigentlichen Implementierung noch darauf hinweisen, dass dieser Beitrag weder die Güte der darüber ermittelten (Pseudo-)Zufallszahlen noch die Vertrauenswürdigkeit des Anbieters bewerten soll. Dieser Beitrag soll lediglich eine sehr einfache und beispielhafte Implementierung für den Fall aufzeigen, dass ihr euch eben für diese Nutzung (beispielsweise für weniger kritische Daten oder aus reinem Interesse) entschieden habt.
Klasse implementieren
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445
public class RandomOrg : RandomNumberGenerator {    private const string ApiMethod = "generateIntegers";    private const string ApiUrl = "https://api.random.org/json-rpc/1/invoke";    private const string ApiVersion = "2.0";
private readonly string apiKey;

public RandomOrg(string apiKey) { this.apiKey = apiKey; }
public override void GetBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length), data, data.Length); }
public override void GetNonZeroBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length, 1), data, data.Length); }

private byte[] SendRequest(int n, byte min = 0) { using(WebClient webClient = new WebClient()) { JavaScriptSerializer js = new JavaScriptSerializer();
dynamic data = js.Deserialize<dynamic>(webClient.UploadString(ApiUrl, js.Serialize(new { jsonrpc = ApiVersion, method = ApiMethod, @params = new { apiKey = this.apiKey, n = n, min = min, max = 255 }, id = DateTime.Now.Ticks })));
if(data.ContainsKey("error")) { throw new Exception(data["error"]["message"]); }
return Array.ConvertAll((object[]) data["result"]["random"]["data"], Convert.ToByte); } }}
Die eigentliche Verwendung gestaltet sich nun kompatibel zur Basisklasse RandomNumberGenerator und entsprechend einfach. Um beispielsweise ein byte-Feld mit fünf Zahlen zu befüllen, kann folgender Aufruf dienen:
Klasse verwenden
01020304
using(RandomOrg numberGenerator = new RandomOrg("@ToDo")) {  // API-Schlüssel eintragen    byte[] randomNumbers = new byte[5];    numberGenerator.GetBytes(randomNumbers);}

Parameter einer Java-Methode per Reflexion ermitteln 👍 👎

Für ein kleines Projekt (mehr dazu in einem separaten Beitrag) war es notwendig, die Parameter von Methoden per Reflexion in Java zu ermitteln. Die entsprechende Funktionalität findet sich im Paket java.lang.reflect.
Parameter einer Java-Methode per Reflexion ermitteln
0102030405060708091011121314151617181920
public static List<Map<String,String>> GetMethodParameterList(    String typeName,    String methodName) throws ClassNotFoundException {    List<Map<String,String>> methodList = new ArrayList<>();
for(Method method : Class.forName(typeName).getMethods()) { if(method.getName().equals(methodName)) { HashMap<String,String> parameterList = new HashMap<>();
for(Parameter parameter : method.getParameters()) { parameterList.put(parameter.getName(), parameter.getType().getCanonicalName()); }
methodList.add(parameterList); } }
return methodList;}
Ähnlich zu LINQ bei C# gibt es für Java sogenannte Streams; wir können obige Implementierung damit beispielsweise wie folgt unter einem funktionalem Paradigma schreiben:
Implementierung mit Java-Streams
010203040506070809101112
public static List<Map<String,String>> GetMethodParameterList(    String typeName,    String methodName) throws ClassNotFoundException {    return Stream.of(Class.forName(typeName).getMethods())        .filter(m -> m.getName().equals(methodName))        .map(m -> Stream.of(m.getParameters()).collect(Collectors.toMap(            k -> k.getName(),            v -> v.getType().getCanonicalName()        )))        .collect(Collectors.toList());}
Die Verwendung gestaltet sich jeweils identisch:
Methode verwenden und Ergebnis auf der Standardausgabe ausgeben
01020304050607
List<Map<String,String>> methodList = GetMethodParameterList(typeName, methodName);
for(Map<String,String> parameterList : methodList) { for(Map.Entry<String,String> parameter : parameterList.entrySet()) { System.out.println(parameter.getValue() + " " + parameter.getKey()); }}
Es gilt schließlich noch eine kleine Besonderheit zu berücksichtigen: Standardmäßig stehen die Bezeichner der Parameter unter Java nicht zur Verfügung, sondern würden schlicht durchnummeriert. Um die tatsächlichen Bezeichner zu erhalten, muss mit der Option -parameters kompiliert werden. Verbreitete Entwicklungsumgebungen wie Eclipse oder NetBeans bieten hierfür entsprechende Einstellungen.

Wir bereits früher angekündigt werde ich zur Reflexion in C# noch einen etwas ausführlicheren Beitrag schreiben und dann dort eine entsprechende Implementierung ähnlicher Funktionalität vorstellen.

DNS-Cache unter Windows und mit C# leeren 👍 👎

Um den DNS-Zwischenspeicher unter Windows zu leeren, gibt es den bekannten Befehl ipconfig /flushdns.

Prinzipiell wäre es zwar möglich, das Programm ipconfig mit dem Argument /flushdns über die Process-Klasse per C# aufzurufen, jedoch ist dies nicht ganz unproblematisch, da die Ausführung längere Zeit dauern kann.

Alternativ hilft uns hier – wieder einmal – die Windows-API in Kombination mit dem DllImport-Attribut:
(Hilfs-)Methode deklarieren und verwenden
01020304050607
public static class DnsUtility {    [DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]    public static extern void FlushCache();}
// DNS-Cache leerenDnsUtility.FlushCache();
Dies ist insbesondere in Verbindung mit der Dns-Klasse nützlich, um aktuelle Daten zu erhalten. Natürlich gibt es einen solchen Zwischenspeicher aber nicht grundlos, d. h. es ist nur äußerst selten ratsam, diesen immer wieder vor einer Namensauflösung zu leeren – Performanzdifferenzen im Rahmen einer Größenordnung sind hier üblich. Im Rahmen des Debuggings oder für besonders kritische Prozesse kann das Vorgehen jedoch durchaus sinnvoll sein.

GET- und POST-Anfragen mit C# 👍 👎

Für viele APIs – aber auch so manchen Test – ist es hilfreich, GET- und POST-Anfragen per HTTP senden zu können. Selbstverständlich ist das auch per C# sehr einfach möglich, wobei je nach gewünschtem Grad der Kontrollierbarkeit der Anfragen verschiedene Abstrahierungen zur Verfügung gestellt werden:

Die umfangreichsten Möglichkeiten (und geringen Abstraktiongrad) bietet HttpWebRequest:
GET per HttpWebRequest
01020304050607
HttpWebRequest webRequest = HttpWebRequest.CreateHttp("…/?key1=val1&key2=val2");
using(HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) { using(StreamReader streamReader = new StreamReader(webResponse.GetResponseStream())) { string response = streamReader.ReadToEnd(); }}
POST per HttpWebRequest
010203040506070809101112131415161718
HttpWebRequest webRequest = HttpWebRequest.CreateHttp("…");{    byte[] data = Encoding.UTF8.GetBytes("key1=val1&key2=val2");
webRequest.Method = "POST"; webRequest.ContentType = "application/x-www-form-urlencoded"; webRequest.ContentLength = data.Length;
using(Stream stream = webRequest.GetRequestStream()) { stream.Write(data, 0, data.Length); }}
using(HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) { using(StreamReader streamReader = new StreamReader(webResponse.GetResponseStream())) { string response = streamReader.ReadToEnd(); }}
Deutlich kompakter lassen sich entsprechende Anfragen per WebClient formulieren:
GET per WebClient
010203
using(WebClient webClient = new WebClient()) {    string response = webClient.DownloadString("…/?key1=val1&key2=val2");}
POST per WebClient
010203040506
using(WebClient webClient = new WebClient()) {    string response = Encoding.UTF8.GetString(webClient.UploadValues("…", new NameValueCollection() {        {"key1", "val1"},        {"key2", "val2"}    }));}
Die aktuellste Möglichkeit mit Fokus auf asynchrone Vorgänge stellt HttpClient dar:
GET per HttpClient
010203
using(HttpClient client = new HttpClient()) {    string response = await client.GetStringAsync("…/?key1=val1&key2=val2");}
POST per HttpClient
01020304050607080910
using(HttpClient httpClient = new HttpClient()) {    HttpResponseMessage responseMessage = await httpClient.PostAsync("…",        new FormUrlEncodedContent(new Dictionary<string, string>() {            {"key1", "val1"},            {"key2", "val2"}        })    );
string response = await responseMessage.Content.ReadAsStringAsync();}
Grundsätzlich bestünde auch noch die Möglichkeit, auf TCP-Ebene unter Anwendung des HTTP-Protokolls vorzugehen – aber das wird man kaum benötigen. Abschließend sei noch erwähnt, dass diese Möglichkeiten nicht für fragliche Aktionen genutzt werden sollte – dazu gehört im Übrigen auch schlicht unnötiger Traffic.

12

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche