Rainer's profileCyrons BlogPhotosBlogListsMore ![]() | Help |
|
|
May 27 Skydrive endlich auch in good ol' germany!Na endlich kann ich auch die eine oder andere Demo-Applikation zum Download stellen! May 25 Dazu benutzt man InterfacesAls Anfänger hatte ich mir lange die Frage gestellt, wozu Interfaces überhaupt dienen. Irgendwie fehlte mir immer das Verständnis dafür. Nachdem ich mich nun einige Zeit mit Design Patterns beschäftigt habe, fiel irgendwann der Groschen. Ich denke, anderen Anfängern geht es auch nicht besser. Vielleicht kann dieser Artikel etwas Licht in die Dunkelheit bringen. Der folgende Artikel setzt ein paar generelle Grundkenntnisse voraus (Instantiierung eines Interfaces, Kenntnis der List<T>-Klasse). Am Ende des Beispiels schreibe ich noch ein paar Worte dazu. using System; using System.Collections.Generic; namespace InterfacesDazu { //====================================================================== class Program { static void Main() { IMyInterface interfaceObject = new MyClass(); Console.WriteLine(interfaceObject.GiveSomethingBack()); interfaceObject = new MyOtherClass(); Console.WriteLine(interfaceObject.GiveSomethingBack()); // Etwas komplexer, dafür eventuell einleuchtender -> List<IMyInterface> myList = new List<IMyInterface>(); myList.Add(new MyClass()); myList.Add(new MyOtherClass()); foreach(IMyInterface item in myList) { Console.WriteLine(item.GiveSomethingBack()); } // <- Beides erzeugt die gleiche Ausgabe. // Verhindert das selbsttätige Schließen des Konsolenfensters. Console.WriteLine("\nPress any key to terminate the program."); Console.ReadKey(); } } //====================================================================== interface IMyInterface { string GiveSomethingBack(); } //====================================================================== class MyClass : IMyInterface { #region IMyInterface Members public string GiveSomethingBack() { return "Hello from MyClass"; } #endregion } //====================================================================== class MyOtherClass : IMyInterface { #region IMyInterface Members public string GiveSomethingBack() { return "Hello from MyOtherClass"; } #endregion } } Na, hat's >>Klick<< gemacht? Immer dann wenn dynamisch verschiedene Klassen auf einen Typ referenziert werden müssen, kann das über ein Interface gelöst werden. List<T> erwartet einen ganz bestimmten Typ - und natürlich nur einen. Setzt man ein Interface ein (in diesem Fall IMyInterface), kann man Instanzen aller Klassen übergeben, die dieses Interface implementieren! Diese Implementierung sorgt nämlich dafür daß alle Objekte in so einer Klasse angelegt werden müssen, die ein Interface anbietet - und macht die Klassen somit kompatibel. Es ist wie ein Schlüssel/Schloß-Prinzip. Klassen stellen unterschiedliche Typen dar (class Auto ist nicht gleich class Person). Durch eine gemeinsame Interface-Implementierung erhalten sie eine SCHNITTSTELLE, die sie zumindest teilweise für andere gleich macht. May 19 Extension Methods am Beispiel von Limit<> und LimitRange<>Wer diesen Artikel sucht, wird sich jetzt vielleicht wundern. Er ist aber nicht fort. Mittlerweile waren es nur vier Blogeinträge, die alle aufeinander aufbauten. Ich habe jetzt alles in diesem Artikel zusammengefasst. May 14 Die Zukunft der Computerbedienung schon jetzt mit Microsoft Surface!
Ich war schon vor Monaten über diesen Link gestolpert, kam aber erst jetzt auf die Idee, darüber etwas in mein Blog zu schreiben. Ein Weltraumteleskop für Jedermann!
Google Earth hat es mit seinem Planetarium-Modus vorgemacht, Microsoft setzt noch einen drauf. May 09 Vergleichsverfahren, FortsetzungWie im Hauptartikel zu sehen war, gibt es verschiedene Verfahren, die im Prinzip aber irgendwie das gleiche machen. Ist es da nicht egal, ob man if/else benutzt oder eine der anderen Operatoren? Statt des ??-Operators kann man wohl auch gleich ? und : benutzen, der Compiler macht sowieso letzteres daraus! Allerdings finde ich die Syntax bei der ??-Variante menschenfreundlicher als das andere - naja, das ist Geschmacksache. Und was ist mit "if/else" im Vergleich zu "?/:" ? using System; namespace ILtest { class Program { static void Main() { int wert = 150; int ergebnis = wert > 100 ? 100 : wert; // if(wert > 100) ergebnis = 100; else ergebnis = wert; } } } Hier das disassemblierte Kompilat im Reflector: internal class Program Der Compiler hat diese Struktur also beibehalten. Schauen wir uns nun den IL-Code (siehe Fußnote) an: //000008: { Wie man sieht, ist die Prüfung mittels "?:" tatsächlich kürzer. In Zeiten von mehreren Gigabyte großem RAM ist das wohl trivial, aber man stelle sich die Abfrage einer Liste mit hunderttausend und mehr Einträgen vor. Das bringt wohl doch einen Geschwindigkeitsvorteil. Fußnote Wie kann man IL sichtbar machen? May 08 VergleichsverfahrenKeine Angst, wir gehen nicht vor Gericht. ;-)
Hier nun eine Liste (mit Beispielen) aller (mir bekannten) Validierungswerkzeuge. using System; namespace Vergleichsverfahren { class Program { static void Main() { Console.WriteLine("Vergleich über if/else"); Console.WriteLine("Geprüft wird, ob eine Zahl groesser 0 ist."); Console.WriteLine("Gib eine Ganzzahl ein:"); // Fehleingaben werden hier nicht behandelt! int number = int.Parse(Console.ReadLine()); Console.WriteLine("Die Zahl ist {0}.", Comparator.IfMethod(number)); //===================================================================================== Console.WriteLine("\n\nVergleich über ? :"); Console.WriteLine("Geprüft wird, ob eine Zahl groesser 0 ist."); Console.WriteLine("Gib eine Ganzzahl ein:"); // Fehleingaben werden hier nicht behandelt! number = int.Parse(Console.ReadLine()); Console.WriteLine("Die Zahl ist {0}.", Comparator.QuestionMethod(number)); //===================================================================================== Console.WriteLine("\n\nVergleich über ??" + "\nDieser Operator ist ein Sonderfall, weil er nur auf null prüft." + "\nIst ein Wert null," + "\nkann er mit diesem Verfahren gegen etwas anderes getauscht werden" + "\n?? wird also immer dann eingesetzt," + "\nwenn ein Empfänger-Objekt null nicht verarbeiten kann."); Console.WriteLine("\nEs wird ein Datenbankzugriff simuliert." + " Der User sucht einen bestimmten Datensatz." + " Das Produkt \"Eiscreme\" wird gefunden, etwas anderes nicht" + "\n(es wird null zurückgegeben)." + "\nNach welchem Produkt suchst du?"); string productName = Console.ReadLine(); // Die Empfängervariable kann null nicht verarbeiten! // Darum muss null in etwas anderes gewandelt werden. Guid result = Comparator.ValidateProduct(productName); if(result.Equals(Guid.Empty)) Console.WriteLine("Datensatz wurde nicht gefunden."); else Console.WriteLine("Datensatz mit der ID {0} wurde gefunden.", result.ToString()); //===================================================================================== Console.WriteLine("\n\nVergleich über Lambda-Expression," + "\nmit Hilfe von Func<T, TResult>"); Console.WriteLine("Es wird ein Druckwächter simuliert." + "Ist der Druck groesser als 5, wird Alarm gegeben."); Console.WriteLine("Wie hoch soll der Druck sein?"); // Fehleingaben werden hier nicht behandelt! int pressure = int.Parse(Console.ReadLine()); bool pressureOK = Comparator.CheckPressureWithLambda(pressure); DisplayPressureValidationResult(pressureOK); //======================================================================================= Console.WriteLine("\n\nVergleich über logischen Operator"); Console.WriteLine("Wieder wird ein Druckwächter simuliert," + "\nnur läuft der Vergleich diesmal über eine logische Operation."); Console.WriteLine("Wie hoch soll der Druck sein (s.o.)?"); // Fehleingaben werden hier nicht behandelt! pressure = int.Parse(Console.ReadLine()); pressureOK = Comparator.CheckPressureWithLogicalOperation(pressure); DisplayPressureValidationResult(pressureOK); } //======================================================================================= // Spätestens wenn ein und der selbe Codeblock mehrmals benutzt wird, // sollte man ihm eine eigene Methode gönnen. private static void DisplayPressureValidationResult(bool pressureOK) { if(pressureOK) Console.WriteLine("Der Druck ist im Limit."); else Console.WriteLine("ACHTUNG! Der Druck ist zu hoch!"); } } //===================================================================================== //===================================================================================== static class Comparator { // Vergleicht durch If-Operator. internal static string IfMethod(int number) { if(number > 0) return "groesser als 0"; return "ist 0"; } // Vergleicht durch ?-Operator. internal static string QuestionMethod(int number) { return number > 0 ? "groesser als 0" : " ist 0"; Jetzt wird es erst richtig interessant! internal static class Comparator { // Methods Das war mal eine Lambda-Expression:
internal static bool CheckPressureWithLambda(int value)
{
Func<int, bool> myFunc = delegate (int x) {
return x <= 5;
};
return myFunc(value);
}
internal static bool CheckPressureWithLogicalOperation(int value)
{
return (value <= 5);
}
internal static Guid? GetProduct(string productName)
{
if (productName.Equals("Eiscreme"))
{
return new Guid?(Guid.NewGuid());
}
return null;
}
internal static string IfMethod(int number)
{
if (number > 0)
{
return "groesser als 0";
}
return "ist 0";
}
internal static string QuestionMethod(int number)
{
return ((number > 0) ? "groesser als 0" : " ist 0");
}
Der ??-Operator ist nicht mehr wiederzuerkennen: May 05 Frühe und späte Initialisierung von SingletonsEin sehr interessanter Artikel, den sich jeder Entwickler mal durchlesen sollte (englisch): May 03 DB-Concurrency Problem mit Hilfe von Timestamp-Feldern lösenZufällig habe ich in Dan Wahlins Blog etwas sehr interessantes gefunden: since the SQL can use the TimeStamp field in the WHERE class to see if any concurrency issues have occurred. Quelle: <http://weblogs.asp.net/dwahlin/archive/2008/02/28/building-an-n-layer-asp-net-application-with-linq-lambdas-and-stored-procedures.aspx> Konkret:
Versucht man nun einen Tupel upzudaten, während zwischendurch schon jemand anderer das Gleiche gemacht hat, kann reagiert werden. May 02 Value Limiter über Extension MethodIch bastle gerade an einer Prozessüberwachung. Beim Auslesen der Prozessorlast fiel mir auf daß die Werte 100 übersteigen können (keine Ahnung warum). Da ich die Last über einen Progressbar grafisch darstellen will, mußte ich einen Limiter dazwischenschalten. Dieses Objekt kann einfach so erstellt werden: public int Limit(int value) { return value > 100 ? 100 : value; } Auf Deutsch: Ist value größer 100? Dann gib 100 zurück, sonst gib den Wert von value zurück. public static class Extensions { public static T Limit<T>(this T value, T maximum) where T : IComparable<T> { return value.CompareTo(maximum) < 1 ? value : maximum; } } Benutzt wird diese Methode dann wie folgt:
Der fertige Code (Cyrons.Extensions.dll wird referenziert): using Cyrons.Extensions; ... int testValue = 150; int limitedResult = testValue.Limit(100); // limitedResult wird 100 sein. Es geht aber noch besser. Wie wäre es, wenn ein Event Abonnenten über Grenzwertverletzungen informiert? Um die Sache rund zu machen, habe ich eine Überladung der Limit<>-Methode geschrieben, bei der man zusätzlich den Absender angeben kann. Somit kann ein Objekt, das sich mit LimitExceeded verbunden hat, jederzeit darüber informiert werden, wenn irgendwo in der Applikation ein Limit überschritten wurde! Und als ich schon einmal dabei war, die Erweiterung zu erweitern, da habe ich auch gleich noch eine weitere Extension Method gebastelt: LimitRange<>. /// <summary> /// Stellt verschiedene Extension-Methoden zur Verfügung. /// </summary> public static class Extensions { /// <summary> /// Tritt ein, wenn ein an Limit oder LimitRange übergebener Wert /// eine Grenzwertverletzung hervorgerufen hat. /// </summary> public static event EventHandler<LimitEventArgs> LimitExceeded; /// <summary> /// Verhindert daß eine Zahl einen Maximalwert überschreitet. /// </summary> /// <typeparam name="T">IComparable</typeparam> /// <param name="value">Wert eines Typs /// der das IComparable-Interface implementiert.</param> /// <param name="maximum">Der Maximalwert, den eine Zahl annehmen darf.</param> /// <returns>Der gefilterte Wert.</returns> /// <example>decimal result = testValue.Limit(100);</example> public static T Limit<T>(this T value, T maximum) where T : IComparable<T> { return value.CompareTo(maximum) < 1 ? value : maximum; } /// <summary> /// Verhindert daß eine Zahl einen Maximalwert überschreitet. /// </summary> /// <typeparam name="T"><see cref="IComparable"/></typeparam> /// <param name="value">Zu prüfender Wert</param> /// <param name="maximum">Der Maximalwert, den eine Zahl annehmen darf.</param> /// <param name="invoker">Die Klasse, die Limit aufgerufen hat /// (sollte "this" sein).</param> /// <returns>Der gefilterte Wert.</returns> ///<example>decimal result = testValue.Limit(100, this);</example> public static T Limit<T>(this T value, T maximum, object invoker) where T : IComparable<T> { if(value.CompareTo(maximum) < 1) return value; else { if(LimitExceeded != null) { LimitEventArgs e = new LimitEventArgs((IComparable)value, (IComparable)maximum); LimitExceeded(invoker, e); } return maximum; } } /// <summary> /// Verhindert daß eine Zahl ein Minimum unterschreitet, /// oder ein Maximum überschreitet. /// </summary> /// <typeparam name="T"><see cref="IComparable"/></typeparam> /// <param name="value">Zu prüfender Wert</param> /// <param name="minimum">Das Minimum, /// welches eine Zahl nicht unterschreiten darf.</param> /// <param name="maximum">Das Maximum, /// welches eine Zahl nicht überschreiten darf.</param> /// <returns>Der gefilterte Wert.</returns> public static T LimitRange<T>(this T value, T minimum, T maximum) where T : IComparable<T> { if(value.CompareTo(minimum) < 0) return minimum; else if(value.CompareTo(maximum) > 0) return maximum; else return value; } /// <summary> /// Verhindert daß eine Zahl ein Minimum unterschreitet, /// oder ein Maximum überschreitet. /// </summary> /// <typeparam name="T"><see cref="IComparable"/></typeparam> /// <param name="value">Zu prüfender Wert</param> /// <param name="minimum">Das Minimum, /// welches eine Zahl nicht unterschreiten darf.</param> /// <param name="maximum">Das Maximum, /// welches eine Zahl nicht überschreiten darf.</param> /// <param name="invoker">Die Klasse, die LimitRange aufgerufen hat /// (sollte "this" sein).</param> /// <returns>Der gefilterte Wert.</returns> public static T LimitRange<T>(this T value, T minimum, T maximum, object invoker) where T : IComparable<T> { if(value.CompareTo(minimum) < 0) { if(LimitExceeded != null) { LimitEventArgs e = new LimitEventArgs( (IComparable)value, (IComparable)minimum, (IComparable)maximum); LimitExceeded(invoker, e); } return minimum; } else if(value.CompareTo(maximum) > 0) { if(LimitExceeded != null) { LimitEventArgs e = new LimitEventArgs( (IComparable)value, (IComparable)minimum, (IComparable)maximum); LimitExceeded(invoker, e); } return maximum; } else return value; } // Weitere Extension-Methods...
}/// <summary> /// Stellt Daten für das LimitExceeded-Event zur Verfügung. /// </summary> public sealed class LimitEventArgs : EventArgs { StackTrace st; /// <summary> /// Initialisiert eine neue Instanz der <see cref="LimitEventArgs"/> Klasse. /// </summary> /// /// <param name="value">The value.</param> /// <param name="maximum">The maximum.</param> public LimitEventArgs(IComparable value, IComparable maximum) { st = new StackTrace(true); /* Index 0: ctor von LimitEventArgs * Index 1: Eventauslöser (ist Limit<T>) * Index 2: Die Methode die Limit<T> verwendet. */ this.InvokerMethod = st.GetFrame(2).GetMethod(); this.Maximum = maximum; this.Value = value; } /// <summary> /// Initialisiert eine neue Instanz der <see cref="LimitEventArgs"/> Klasse. /// </summary> /// <param name="value">The value.</param> /// <param name="minimum">The minimum.</param> /// <param name="maximum">The maximum.</param> public LimitEventArgs(IComparable value, IComparable minimum, IComparable maximum) { st = new StackTrace(true); this.InvokerMethod = st.GetFrame(2).GetMethod(); this.Maximum = maximum; this.Minimum = minimum; this.Value = value; } /// <summary> /// Gibt die Methode zurück, /// durch die das LimitExceeded-Event ausgelöst wurde. /// </summary> public System.Reflection.MethodBase InvokerMethod { get; private set; } /// <summary> /// Ruft das an die Limit-Methode übergebene Maximum ab. /// </summary> public IComparable Maximum { get; private set; } /// <summary> /// Ruft das an die Limit-Methode übergebene Minimum ab. /// </summary> public IComparable Minimum { get; private set; } /// <summary> /// Ruft den an die Limit-Methode übergebenen Wert ab. /// </summary> public IComparable Value { get; private set; } } Dazu wieder ein Demo class ClassA { static void Main() { ClassB anotherLimitBreaker = new ClassB(); ClassC rangeBreaker = new ClassC(); ClassD observer = new ClassD(); observer.AttachToLimitExceededEvent(); int testValue = 150; // Die Version ohne invoker und damit ohne Event: int limitedResult = testValue.Limit(100); // limitedResult wird 100 sein. Console.WriteLine("limitedResult ist {0}\r\n", limitedResult.ToString()); anotherLimitBreaker.ExceedLimit(); //Alles OK rangeBreaker.ExceedRange(1, 0, 100); // Minimum unterschritten rangeBreaker.ExceedRange(-1, 0, 100); // Maximum überschritten rangeBreaker.ExceedRange(110, 0, 100); // Verhindert das selbsttätige Schließen des Konsolenfensters. Console.WriteLine("\nPress any key to terminate the program."); Console.ReadKey(); } } //====================================================================== // Ein weiterer Limit-Brecher class ClassB { public void ExceedLimit() { double testValue = 3.141592654; // Die Version mit invoker und dadurch mit Event: double limitedResult = testValue.Limit(2.0, this); } } //====================================================================== // A range breaker class ClassC { public void ExceedRange(int value, int minimum, int maximum) { int result = value.LimitRange(minimum, maximum, this); } } //====================================================================== // Observer class ClassD { public void AttachToLimitExceededEvent() { Extensions.LimitExceeded += new EventHandler<LimitEventArgs>(LimiterProbe); } protected void LimiterProbe(object sender, LimitEventArgs e) { Console.WriteLine( "In {0}.{1} gab es eine Grenzwertverletzung.", sender.ToString(), e.InvokerMethod.ToString()); Console.WriteLine("Die Daten:"); Console.WriteLine("Der Wert: {0}", e.Value); if(e.Minimum != null) Console.WriteLine("Das Minimum: {0}", e.Minimum); Console.WriteLine("Das Maximum: {0}", e.Maximum); // Wenn man Value, Minimum und Maximum castet, kann man damit auch rechnen: try { if(Convert.ToDecimal(e.Value) > Convert.ToDecimal(e.Maximum)) Console.WriteLine("Der Wert überstieg das Maximum um {0}%\r\n", Math.Round((((Convert.ToDecimal(e.Value) / (Convert.ToDecimal(e.Maximum)) * 100) - 100)), 1)); else Console.WriteLine(); } catch(Exception) { Console.WriteLine("Berechnung konnte nicht durchgeführt werden!"); } // Erst wenn das Abo nicht mehr gebraucht wird: //DetachStaticEvents(); } /// <summary> /// Löst Verbindungen für statische Events auf, um Speicherlecks zu verhindern. /// </summary> protected void DetachStaticEvents() { /* Laut MSDN ergeben sich Speicherlecks, * wenn man Verbindungen zu statischen Events nicht wieder auflöst * (siehe ThreadException Event im MSDN). */ Extensions.LimitExceeded -= new EventHandler<LimitEventArgs>(LimiterProbe); } } Das (oder die?) Demo erzeugt folgenden Output: limitedResult ist 100 In ExtendingLimits.ClassB.Void ExceedLimit() gab es eine Grenzwertverletzung. In ExtendingLimits.ClassC.Void ExceedRange(Int32, Int32, Int32) gab es eine Grenzwertverletzung. In ExtendingLimits.ClassC.Void ExceedRange(Int32, Int32, Int32) gab es eine Grenzwertverletzung. Press any key to terminate the program. |
|
|