LINQ-Provider als D(o)I(t)Y(oursef)

by Bernhard Wurm 5. November 2010 11:35

LINQ funktioniert mit allen Objekten, welche IEnumerable implementieren. Richtig? Richtig!
Doch dies ist nicht unbedingt ein muss!

Verschiedene Linq-Provider ermöglichen den Zugriff auf verschiedenste Datenquellen. Z.B.: Linq2Sql, Linq2Xml, Linq2Sap, Linq2Twitter und so weiter. Auch selbst kann ein entsprechender Provider gebaut werden indem das Interface IQueryProvider implementiert wird. Doch wer einen entsprechenden Provider implementiert wird viele Methoden zu implementieren haben, damit die Linq-Syntax auch wirklich unterstützt wird und keine Laufzeitfehler auftreten. Es sind nicht nur die Methoden select und where zu implementieren sondern auch groupby etc. werden diese nicht implementiert, so kommt es zu einem Laufzeitfehler.

Wie kann für eine eigene Klasse eine einfache, eingeschränkte Linq-Abfrage abgesetzt werden ohne dass ein eigener großer Provider implementiert wird und gleichzeitig der Compiler Prüfungen durchführen kann.

Interessanter Punkt:

Was man mit LINQ abfragen will muss nicht IEnumerable<T> implementieren! Es muss lediglich eine GetEnumerator()-Methode verfügbar sein. Ja nicht mal für foreach muss IEnumerable implementiert werden. Auch hier reicht eine GetEnumerator()-Methode.

Spannendes zu LINQ:

Was muss ich machen, damit meine Klasse MyContainerClass mit Linq abfragen kann?

var data = new MyContainerClass();
var result = from d in data
where d.Name == "Third"
select d;

Dabei soll jedoch lediglich select und where verfügbar sein, da ich mich z.B.: über groupby nicht kümmern will. Es soll aber wie gesagt auch nicht zu einem Runtime-Fehler führen, sondern der Compiler prüfen.

Nun ja. Als erstes wird die Klasse mit der Eigenschaft Name, sowie der GetEnumerator-Methode.

class MyContainerClass  {
//Simulate data :)
static MyContainerClass[] data = new MyContainerClass[] {
new MyContainerClass() { Name = "First" },
new MyContainerClass() { Name = "Second" },
new MyContainerClass() { Name = "Third" },
new MyContainerClass() { Name = "Fourth" }
};

public string Name {get; set;}

public IEnumerator<MyContainerClass> GetEnumerator() {
foreach (var item in data)
yield return item;
}
}

So weit so einfach. Doch wie bekommen wir nun die echte LINQ Unterstützung? Ebenfalls ganz einfach: Man implementiere lediglich Methoden mit einem bestimmten Namen und einer bestimmten Signatur:

class MyContainerClass  {
  //Simulate data :)
  static MyContainerClass[] data = new MyContainerClass[] {
    new MyContainerClass() { Name = "First" },
    new MyContainerClass() { Name = "Second" },
    new MyContainerClass() { Name = "Third" },
    new MyContainerClass() { Name = "Fourth" }
  };
  public string Name {get; set;}
  public IEnumerator<MyContainerClass> GetEnumerator() {
    foreach (var item in data)
      yield return item;
  }
  public IEnumerable<MyContainerClass> Where(Func<MyContainerClass, bool> predicate) {
    return data.Where(predicate);  //Simulate where by just delegate it
  }
  public IEnumerable<MyContainerClass> Select(Func<MyContainerClass, MyContainerClass> selector) {
    return data.Select(selector);  //Simulate select by just delegate it
  }
}

FERTIG! Das ist es. Es müssen nur die Methoden vorhanden sein und der Compiler lässt die LINQ-Syntax zu.

Man lernt nie aus!

Categories: Programmiersprachen | C#

Spannende Cast-Implementierung in C#

by Bernhard Wurm 14. September 2010 08:32

Jeder der mit der Software Entwicklung beginnt lernt relativ schnell zwischen verschiedenen Datentypen umzuwandeln und zu casten. Von Object nach String. Von double zu int usw.

Doch wie wird eigentlich zwischen komplexeren Datentypen ein Cast durchgeführt?
Also von Duration nach Timespan? Von ServiceObject to BusinessEntity?
Als kleines Beispiel wär doch folgendes echt nett:

MyPoint p1 = new MyPoint();
p1.X = 10.1F;
p1.Y = 3.5F;
//Cast MyPoint to a string.
string pointInfo = (string)p1;

Oder noch viel schöner:

MyPoint p1 = new MyPoint();
p1.X = 10.1F;
p1.Y = 3.5F;
//Directly assign MyPoint object to a string.
string pointInfo1 = p1;

C# bietet hier tatsächlich entsprechende Operator-Implementierungen:
Für die implizite Implementierung

class MyPoint {
	public float X { get; set; }
	public float Y { get; set; }
	/// <summary>
	/// Allow the cast from a MyPoint object to a string. Allow an implicit cast!
	/// </summary>
	public static implicit operator string(MyPoint p) {
		return String.Format("{0};{1}", p.X, p.Y);
	}
	/// <summary>
	/// Cast from a string to a MyPoint-object
	/// </summary>
	public static implicit operator MyPoint(string s) {
		if (s == null)
			return null;
		if (s.IndexOf(';') == -1)
			return new MyPoint();
		//Get values
		float x = float.Parse(s.Split(';')[0]);
		float y = float.Parse(s.Split(';')[1]);
		return new MyPoint() { X = x, Y = y };
	}
}

Dadurch sind folgende Aufrufe möglich:

MyPoint p2 = "10.1;4";
string info = p2;

Eine feine Sache!
Tauscht man nun bei der operator-Implementierung das Wort “implicit” durch “explicit” sind explizite Casts möglich.

VC# Express 2010 in Deutsch ist da!

by Bernhard Wurm 30. April 2010 10:10

Es ist so weit! An alle die auf Visual C# Express Edition 2010 auf DEUTSCH gewartet haben. Seit heute ist es unter http://www.microsoft.com/germany/express/ verfügbar.

csharp_logo

Was heißt das für mich? Nun ja, obwohl das Buch Programmieren Lernen! ohnehin auch 2010 noch Gültigkeit besitzt wirken so manche Screenshots nun wohl etwas verstaubt. Also ran an die Arbeit und das Buch entstauben!

Immerhin soll es doch nicht passieren, das jemand in der Buchhandlung glaubt das Buch sei veraltet.

Wie gesagt das Buch ist nach wie vor aktuell!