C# – Operatorüberladung
Entschuldigt bitte meine lange Abwesenheit. Ich hatte viel mit der Uni zu tuen und konnte deshalb keine neuen Beiträge verfassen.
Ich hoffe der Blog hat nicht all zu sehr darunter gelitten.
In diesem Post soll es um die Operatorenüberladung gehen. Ursprünglich kommt dieser Mechanismus aus C++ und wurde (glücklicherweise) mit in die Sprachfeatures von C# übernommen.
Zuerst möchte ich klären was es damit überhaupt auf sich hat.
Das Überladen von Methoden kennt ja sicherlich jeder. Es beschreibt den Mechanismus das man eine Methode, mit gleichen Funktionsnamen, verschiedene Parameterlisten verpassen kann.
Genau das selbe kann man auch mit Operatoren machen. Eine Liste der Überladbaren Operatoren findet ihr hier.
Das Überladen von Operatoren ist eine heikle Angelegenheit, es birgt zwar eine Menge Potential und ist ein mächtiges Sprachfeature, genauso gut kann man es auch vollkommen falsch verwenden.
Wenn ihr C# programmiert macht ihr von überladenen Operatoren sehr oft Gebrauch.
Wenn ihr zum Beispiel die DateTime Struktur benutzt, dann könnt ihr ein DateTime Objekt mit einem DateTime Objekt vergleichen, Zeitspannen auf ein DateTime Objekt dazuaddieren und Zeitspannen abziehen. Das alles könnt Ihr nur machen, weil die passenden Operatoren(+, -, ==, != usw) dazu überladen wurden.
Warum hab ich aber gesagt dass, das Überladen von Operatoren heikel sein kann? Möchte man einen Operator überladen, sollte man sich vorher genaustens Gedanken darüber machen ob es für die darunter liegende Klasse wirklich sinnvoll ist diejenigen Operatoren zu überladen. Man sollte immer folgende Regel beachten: Überlade ich Operatoren, dann muss die Benutzung, im Zusammenhang mit der Klasse, so intuitiv gestaltet sein wie möglich. Die DateTime Struktur zeigt das was ich mit Intuitiv meine.
DateTime now = DateTime.Now; now = now + TimeSpan.FromDays(5);
Was bei der Addition entsteht ist natürlich ein DateTime Objekt welches 5 Tage von heute dazu addiert bekäme.
Unintuitiv wäre an dieser Stelle wenn ein komplett Wirres Datum entstehen würde.
Als nächstes möchte ich mit euch eine minimalistische Version einer Klasse entwickeln welche zum Rechnen mit Rationalen Zahlen dient.
In dieser Klasse werde ich diverse Operatoren überladen, weil sich das bei einer solchen Klasse mehr als anbietet. Folgende Features wollen wir mit der Klasse unterstützen
Fraction f = 0.2; Fraction f = 0.5; Fraction f2 = 0.1; Fraction f3 = f + f2; Fraction f +=0.59;
Das sind eine Menge Anforderungen die wir da unterstützen wollen. Nachfolgend will ich euch zeigen wir Ihr den ersten Fall unterstützen könnt. Dieser Operator nennt sich der implicit-operator. Dieser Operator wird bei jeglichen Konvertierungen und Casts aufgerufen die Implizit durchgeführt werden können. Wichtig bei diesem Operator: Es darf keine Exception geworfen werden während des Konvertierungsvorgangs. Sollte man das nicht sicherstellen können, sollte man den explicit-operator Überladen.
Operatoren werden IMMER als statische Methoden einer Klasse definiert. Der implicit-operator für unsere Klasse sehe zum Beispiel wie folgt aus:
public static implicit operator Fraction(double number)
{
return new Fraction(number);
}
Ich werde euch das Projekt mit an den Post hängen sodass ich hier nur die Operator-Definitionen präsentiere da ich nicht all den anderen Code hier mit Posten möchte um nicht vom Hauptthema abzulenken.
Wie Ihr sehen könnt definiere ich die Methode zuerst als Statische Methode der Klasse, danach kommt das Schlüsselwort implicit gefolgt vom operator Schlüsselwort und dem Rückgabewert der bei dieser impliziten Konvertierung zurückgegeben werden soll. In den Klammern steht dann ausgehend von welchem Typ wir die Konvertierung durchführungen wollen.
Möchten wir noch long-Zahlen als Konvertierungskandidaten unterstützen, so müssen wir eine weitere Überladung anbieten.
public static implicit operator Fraction(long number)
{
return new Fraction(number);
}
Als nächstes möchte ich euch noch die Binären Operatoren, speziell die +-operatoren präsentieren.
Wollen wir zwei Bruch-Objekte miteinander addieren so muss das wie folgt aussehen:
public static Fraction operator +(Fraction frac, Fraction otherFrac)
{
if (frac == null)
throw new ArgumentNullException("frac", "Bruchobjekt darf nicht 'null' sein.");
return frac.Add(otherFrac);
}
Innerhalb der Klammer stehen nun der linke Operand (erstes Argument) und rechte Operand (zweites Argument). Als Rückgabe erhalten wir hier wieder ein Bruch-Objekt. Natürlich könnte man hier auch ein double zurückgeben, doch das würde dann schon wieder unintuitiv werden.
Was aber wenn ich ein Bruchobjekt mit einer double-Zahl addieren will. Dann brauchen wir nur das zweite Argument in der Parameterliste ändern:
public static Fraction operator +(Fraction frac, double number)
{
if (frac == null)
throw new ArgumentNullException("frac", "Bruchobjekt darf nicht 'null' sein.");
return frac.Add(number);
}
Es bietet sich meistens an, für die jeweiligen Operatoren die passenden Funktionen zur Verfügung zu stellen, dann brauch man den Aufruf des Operators nur an die Funktion weiterleiten.
Ich hoffe ich konnte euch das Thema Operatoren anhand dieses Beispiels etwas näher bringen. Ich halte es für unnötig euch noch die ganzeren anderen Operatoren zu präsentieren, weil das alles der gleiche Aufbau ist.
Bruch Klasse
C# – Constraints
In dem Artikel über Generische Funktionen habe ich bereits angedeutet was man mit Generics alles machen kann. Etwas sehr verspätet möchte ich jetzt, wie damals versprochen, die Constraints nachschieben. Ich bitte den verspäteten Post zu entschuldigen.
Was bedeutet das Wort Constraint überhaupt?
Ein Constraint ist eine Einschränkung. Im .NET Bereich heißt das, dass Typen oder “Fähigkeiten” von Typen eingeschränkt werden.
Jetzt stellt sich aber die nächste Frage: Warum sollte man so etwas tuen?
Die Frage kann man wie folgt beantworten. Manchmal gibt es Momente, wo man generische Funktionen oder Klassen schreibt in denen man von dem Typ-Parameter ein gewisses Verhalten voraussetzen muss, um die Funktionalität bieten zu können die man bieten will. Das heißt zum Beispiel folgendes:
public class Generic<T>
{
public T Value{get;set;}
public Generic()
{
this.Value = new T();
}
}
Dieser Code würde mit einer Fehlermeldung des Compilers quittiert werden. Doch warum? Ganz einfach deshalb, weil die Instanzieierung einen Parameterlosen Konstruktor erfordert und der Compiler nicht garantieren kann das dieser für jeden Typ,welcher als Typ-Parameter übergeben wird, existiert. Wenn wir aber dennoch ein Objekt erstellen wollen, müssen wir den Typ-Parameter so einschränken das der später eingesetzte Typ auf jedenfall einen Parameterlosen Konstruktor anbieten muss. Das machen wir wie folgt:
public class Generic<T> where T : new()
{
public T Value{get;set;}
public Generic()
{
this.Value = new T();
}
}
Über das where-Schlüsselwort können wir den Typ-Parameter einschränken. Wenn wir uns nun aber vorstellen das wir nicht nur einen Parameterlosen-Konstruktor brauchen, sondern auch noch gewisse Methoden, wie zum Beispiel die CompareTo-Methode aus dem IComparable
hierbei ist jedoch die Reihenfolge der Constraints. Zuerst kommen alle jene Constraints die den Typen näher Beschreiben und als aller Letztes das new() Constraint. So dass unser Code jetzt so aussehen könnte:
public class Generic<T> where T : IComparable<T>, new()
{
public T Value { get; set; }
public Generic()
{
this.Value = new T();
}
}
Natürlich kann man anstatt von Interfaces auch Klassen als Constrains angeben.
Möchte man nun noch zusätzlich definieren das unser Typ-Parameter keine Value-Types annehmen darf so muss man das mit dem class Schlüsselwort tuen. Auch hierbei ist zu beachten, das class-Schlüsselwort muss vor allen anderen Constrains kommen, sozusagen als erstes. Gegenteilig kann man auch das struct-Schlüsselwort benutzen um den Typ-Parameter nur auf Value-Types einzuschränken. Das vollständige Beispiel sieht dann wie folgt aus:
public class Generic<T> where T : class,IComparable<T>, new()
{
public T Value { get; set; }
public Generic()
{
this.Value = new T();
}
}
Constrains sind nicht an generische Klassen gebunden, sie können genauso gut auf generische Funktion angewendet werden.
Was leider nicht möglich ist, über den new() Constraint Parameter behaftete Konstruktor anzugeben, was ich persönlich sehr Schade finde.
Vermutlich habt ihr schon von, der seit 4.0 neuen Zusatz Technologie Reactive Extension gehört. Da ich sehr angetan von dieser Technologie bin, wurde ich auch schon öfter gefragt ob ich nicht mal ein sinnvolles Beispiel zeigen könnte. Auf die Frage musste ich bisher immer mit einem “Naja nicht wirklich” antworten. Doch mir ist letztens etwas eingefallen was es vielleicht etwas schöner erklärt. Als Beispiel Objekt habe ich mir den BackgroundWorker ausgewählt. Wenn man mit diesem gearbeitet hat, musste man immer mindestens 3 Eventhandler implementieren und sich um deren Logik kümmern. Mit Rx können wir das auf ein Eventhandler reduzieren. Mit Rx ist es nämlich unter anderem Möglich Events als ObservableCollection darzustellen und diese zu konsumieren. Vorweg möchte ich erwähnen das es nötig ist sich Rx zu installieren und die Dlls System.CoreEx sowie System.Reactive einzubinden, ansonsten wird das Projekt nicht bauen.
Kommen wir nun zum interessanten Teil:
static void Main(params string[] args)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
var progressChanged = Observable.FromEvent<ProgressChangedEventArgs>(bw, "ProgressChanged");
var wokerCompleted = Observable.FromEvent<RunWorkerCompletedEventArgs>(bw, "RunWorkerCompleted");
var percentage = from p in progressChanged
.TakeUntil(wokerCompleted).Timestamp()
select new
{
Time = p.Timestamp,
Value = p.Value.EventArgs.ProgressPercentage
};
percentage.Subscribe(onNext => Console.WriteLine(string.Format("{0} | {1}", onNext.Time, onNext.Value)));
Console.ReadKey();
}
static void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker b = sender as BackgroundWorker;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
b.ReportProgress(10 * i);
}
}
Die erste wichtige Stelle ist folgende:
var progressChanged = Observable.FromEvent<ProgressChangedEventArgs>(bw, "ProgressChanged"); var wokerCompleted = Observable.FromEvent<RunWorkerCompletedEventArgs>(bw, "RunWorkerCompleted");
Hier findet die Definition unserer Observables statt von welchen wir später konsumieren wollen. Wir müssen hier die Eventargs der jeweiligen Events angeben, das Objekt von welchem wir konsumieren wollen und das eigentliche Event was angesprochen werden soll.
Nun können wir über die Linq eine sehr intuitive Abfrage starten welche es uns ermöglicht solange von dem progressChanged Event zu konsumieren bis das workerCompleted Event eintritt. Da Rx ein komplett auf dem Observer Pattern beruht müssen wir uns jetzt noch für Observer einschreiben(Subscriben) um die Änderungen mitzubekommen.
Somit haben wir jetzt nur den Event-Handler DoWork aber die anderen beiden, progressChanged und workerCompleted, welche normaler Weiße noch nötig wären um das komplette BackgroundWorker Pattern zu implementieren, wurden durch die Eventbasierten Observables in einem Rutsch abgearbeitet.
Ziel dieses Postes war es nicht das Rx-Framework zu erläutern, dafür gibt es die Seite welche das sehr gut durch begleitende Webcasts und Material bereits tut, sondern eher um eine mögliche Verwendung von Rx.
Vielen ist das sicher schon öfters passiert das man eine Combobox hat und man bindet an diese Combobox eine Collection von Benutzerdefinierten Objekten.
Im Anschluss möchte man das “combobox.SelectedItem” mit einem Benutzerdefinierten Objekt aus dieser Collection setzen.
Doch was man zu sehen bekommt verwirrt einen doch sehr: Das Item wird nicht gesetzt wie man das möchte.
Woran liegt das?
Die Comobox macht einen Equals-Vergleich jedes Items in der ItemsSource Collection mit dem Item was man über SelectedItem gesetzt hat.
Wenn dieser Equals-Vergleich jedoch fehl schlägt dann wird das Item einfach nicht gesetzt.
Beheben kann man dieses Verhalten indem man einmal das IEquatable-Interface implementiert und die Equals-Methode von object überschreibt.
Als kleines Beispiel habe ich hier eine Test-Klasse vorbereitet.
public class Test : IEquatable<Test>
{
public string Title { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Test);
}
public override int GetHashCode()
{
return this.Title.GetHashCode();
}
#region IEquatable<Test> Member
public bool Equals(Test other)
{
return this.Title.Equals(other.Title);
}
#endregion
}
Wichtig hierbei ist, das man ein Kriterium für den Equals-Vergleich benutzt was immer eindeutig ist, zu Demonstrationszwecken hab ich hier jetzt den Titel genommen, was sicherlich nicht das beste Paradebeispiel ist. Außerdem ist darauf zu achten das wenn ihr Equals überschreibt, IMMER GetHashCode() überschreiben müsst. Das müsst ihr so machen das wenn objectB.Equals(objectA) = True ist auch objectA.GetHashCode() == objectB.GetHashCode() = True ist.
Momentan bin ich mit einem privaten Projekt beschäftigt und wollte euch an dem Prototypen sowie dem weiteren Verlauf der Entwicklung teilhaben lassen.
Worum geht es überhaupt?
In dem Blogbeitrag von Fabian Moritz wird ja sehr anschaulich erklärt wie man das Ribbon von SharePoint 2010 individuell anpassen kann.
Da ich das auf meiner momentanen Arbeit auch des öfteren machen musste und mir die ganze XML-Konfiguration irgendwann zu umständlich und zu Fehleranfällig wurde, hab ich mir vorgenommen einen Designer/Editor zu schreiben der genau das Visualisiert was man vorher sonst immer nur über XML machen konnte.
Was für Ansprüche stelle ich nun an diese Anwendung?
- Visualisierung aller SharePoint Ribbon Elemente die auf einem SharePoint Server 2010 wiederzufinden sind.
- Hinzufügen neuer Ribbon-Controls, sowie die Konfiguration jener.
- Entfernen vordefinierter Ribbon-Elemente aus der Ansicht.
- Deployment auf dem SharePoint Server
- Wenn das Tool nur Lokal benutzt wird, soll ein Packaging zu einem .wsp File möglich sein. Um dieses dann Deployen zu können.
Der aktuelle Prototyp unterstützt momentan das Laden der SharePoint-CommandUI.xml und die Visualisierung der Tabs samt Gruppen und Controls mit zugehörigen Bildern und Tooltips.
Weiterhin gibt es schon zwei Tabs in der Haupt-Ribbon in welcher dann die Zuordnung neuer Controls geschehen und das Deployment bzw. Packing stattfinden soll.
Als nächstes möchte ich ein paar Screenshots veröffentlichen:
Das Hauptfenster im Initial Zustand sieht wie folgt aus.

Auf der linken Seite erkennt man jegliche Tabs die in einem SharePoint-Ribbon gibt. Durch anklicken auf einen dieser Elemente im Baum, kann man sich jetzt die Details zu dem Tab, also die Gruppen und Controls in der rechten Ribbon anzeigen lassen.

Das Deployment und das Packaging findet im zweiten Tab der Hauptribbon statt. Noch sind das Prototyp-Buttons, das heißt bis zum Release kann sich da noch einiges ändern.

Die ‘Add’ und ‘Remove’ Buttons die ihr sicherlich schon entdeckt habt, sollen dazu dienen entweder ein neues Benutzerdefiniertes Control hinzuzufügen oder ein vorhandenes zu entfernen.
Geplant ist unter anderem noch die Funktion das man auch Java-Script-Code für die Commands hinterlegen kann.
Ich hoffe ich konnte euch einen kleinen Einblick in mein Vorhaben geben, wenn jemand Anregungen oder Frage hat, dann schreibt mir einfach eine Email, ich beantworte sie auf jeden Fall zeitnah.
Ich möchte mich ganz herzlich bei Christopher Wolf, einem guten Arbeitskollegen und Freund, für die Unterstützung bei den Grafiken bedanken.

