Laden...

[Artikel] C# 3.0: Spracherweiterungen

Erstellt von Noodles vor 16 Jahren Letzter Beitrag vor 16 Jahren 38.112 Views
N
Noodles Themenstarter:in
4.644 Beiträge seit 2004
vor 16 Jahren
[Artikel] C# 3.0: Spracherweiterungen

Automatische Eigenschaften
Bei automatischen Eigenschaften handelt es sich um ein Feature, bei dem der Compiler automatisch die privaten Felder einer Eigenschaft setzt. Der ein oder andere hat sich vielleicht schon über das "seltsame" Verhalten des Codesnippets in Visual Studio 2008 gewundert,

// bisher
private string firstName;
public string FirstName
{
    get { return firstName; }
    set { firstName = value; }
}

// ab Visual Studio 2008
public string FirstName { get; set; }

Erweiterungsmethoden
Durch Erweiterungsmethoden können zum Beispiel zu versiegelten Typen Methoden hinzugefügt werden. Es bestand das Problem auf IEnumerable<T> Methoden aufzurufen. Allerdings kann man ja dem Interface keine Methoden nachträglich hinzufügen. Es sei denn, man nimmt in Kauf das ein gewisse Anzahl von Anwendungen mit diesem Framework nicht mehr funktionieren würden. Erweiterungsmethoden müssen in einer statischen Klasse, als public und statisch definiert sein. Der erste Parameter wird mit einem this vor dem Datentyp gekennzeichnet. Daran und an dem Schlüsselwort static erkennt der Compiler die Erweiterungsmethode.

Beispiel für eine Erweiterungsmethoden

public static bool IsInRange( this int source, int min, int max )
{
    return ( source > min && source < max );
}
         
// auf jedes IEnumerable<T> anwendbar.
public static T Where<T>( this IEnumerable<T> source, Func<T, bool> func )
{
    // ...
}

Objektinitialisierer
Objekte werden ja normalerweise Konstruktoren oder Properties initialisiert. Das funktioniert in C# 3.0 etwas einfacher. Das ist aber keine Magie, auch keine neuen MSIL Anweisungen. Der Compiler wandelt es um und ruft einfach nur die Setter auf.

Person person = new Person { FirstName = "Max", LastName = "Mustermann" };

Die Konstruktorklammern müssen nicht, können aber mitgeschrieben werden. Es kann auch ein benutzerdefinierter Konstruktor erstellt werden, um die Initialisierung eines Members zu erzwingen.

Collectioninitialisierer
Wie es Objektinitialisierer gibt, gibt es auch Collectioninitialisierer. Was nun selbsterklärend sein sollte.

List<Person> persons = new List<Person>
{
    new Person { FirstName = "Max", LastName = "Mustermann" },
    new Person { FirstName = "Tina", LastName = "Tester" }
};

Implizit typisierte lokale Variablen
In C# 3.0 wird bzw. kann der Datentyp einer Variablen automatisch vom Compiler bestimmt werden. Dies geschieht über das Schlüsselwort var. Vorweg, diese Art der Zuweisung ist typsicher. Ist einer Variable einmal ein Typ vom Compiler zugewiesen worden, kann man dieser keinen anderen Datentyp mehr zuweisen.

var intValue = 1;
Console.WriteLine( intValue.GetType() ); // System.Int32
//intValue = 1.2; // Fehler

Es sind aber ein paar Dinge zu beachten.
- var darf nicht verwendet werden:
*als Member in Klassen und Strukturen

*als Funktionsparameter

*als Rückgabewert einer Funktion

  • var muss bei der Deklaration sofort initialisiert werden
    • var darf nicht mit null initialisiert werden

Anonyme Typen
Aus LINQ Abfragen können von Objekten einige, alle oder mehr Eigenschaften in einem neuen Objekt zurückgegeben werden.

Beispiel:

IEnumerable<PersonWithFullName> query = from c in customers
                                        select new PersonWithFullName
                                        {
                                            FirstName = c.FirstName,
                                            LastName = c.LastName,
                                            FullName = c.FirstName + " " + c.LastName
                                        };

Dafür habe ich eine Klasse PersonWithFullName geschrieben. Wie man sich schnell vorstellen kann, kann das sehr ausufernd werden und zu einer "Klassenexplosion" führen. Dafür wurden anonyme Typen eingeführt. Bei anonymen Typen legt der Compiler den Typ an.

var query = from c in customers
            select new { c.FirstName, c.LastName, FullName = c.FirstName + " " + c.LastName };

Es wird nach dem new einfach ein Objekt initilisiert und einer Variable mit dem Typ var zugewiesen. Daraus entsteht in diesem Fall ein IEnumerable<%TypDesAnonymenTypen%>.

Lambda Expressions
Der Nachfolger von anonymen Methoden. Eine Art Funktionszeiger. Das im ersten Moment auffälligste an einer Lambda Expression ist der Doppelpfeil ( => ). Links von dem Doppelpfeil steht die Parameterliste und rechts der Ausdruck. Der Rückgabetyp wird aus dem Typ des Ausdrucks bestimmt.

int[] values = { 1, 3, 4, 6, 88, 66, 453 };
Array.ForEach( values, i => Console.WriteLine( i ) );

Das i vom Typ int sein muss, kann der Compiler aus values ableiten. Diese Lambda Expression entspricht folgender anonymen Methode:

Array.ForEach<int>( values, delegate (int i) { Console.WriteLine(i); } );

Diese anonyme Methode kann man auch folgendermaßen ausdrücken.

Array.ForEach<int>( values, ShowNumbers );

private void ShowNumbers( int i )
{
    Console.WriteLine( i );
}

Weiteres Beispiel für Lambda Expressions:

( a, b ) => a + b
// Zwischenspeichern
Func<int, int, int> myFunc = ( a, b ) => a + b;

Func ist ein delegate der mit dem .NET Framework 3.5 eingeführt wird und in vielen Erweiterungsmethoden zum Einsatz kommt.

Bei den meisten Spracherweiterungen sieht man die Nützlichkeit und den Sinn wohl erst im Einsatz mit LINQ.

Suchhilfe: Spracherweiterung

4.506 Beiträge seit 2004
vor 16 Jahren

Hallo Noodles,

vielen Dank für Deine Beispiele, so kann man sich neue Sprachfeatures auch mal ohne gleich ein Buch kaufen zu müssen ausprobieren.

Eine kleine Frage hätte ich doch zu automatischen Eigenschaften:

Woher weiß der Compiler welches private Feld er denn hernehmen muss. Gibt es da irgendwelche Namenskonventionen, die man einhalten muss (entsprechend, weil bei mir z.B. alle privaten Felder mit "m_" beginnen, die Eigenschaften entsprechend natürlich nicht?

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

6.862 Beiträge seit 2003
vor 16 Jahren

Bei welchem Feature meinst du norman_timo?

Den automatischen Properties? Falls ja, da legt man gar keine privaten Felder mehr an, das macht der Compiler automatisch. Sprich das Feature nutzt nur was wenn man wirklich nur stur Variablen durch Properties veröffentlichen will, sobald eigene Logik in die Properties rein soll, schreibt man sie ganz normal wie gehabt.

Baka wa shinanakya naoranai.

Mein XING Profil.

F
722 Beiträge seit 2005
vor 16 Jahren

hallo zusammen,

wie kann ich dem property denn dann in der klasse die es definiert, einen wert zuweisen, wenn ich den setter entferne?

also sowas wie



class MyClass
{
 public int Zahl { get; }

 public MyClass()
 {
    Zahl = 2; // Das geht nicht...
 } 
}


falls es keinen zugriff auf das private feld gibt, dürfte das ja gar nicht funktionieren, oder? Ich nehme also mal an, dass man bei automatischen Properties den setter nicht entfernen darf?

gruß

6.862 Beiträge seit 2003
vor 16 Jahren

Original von feadur
Ich nehme also mal an, dass man bei automatischen Properties den setter nicht entfernen darf?

So ist es, bei automatisch implementierten Properties muss immer beides vorhanden sein.

Baka wa shinanakya naoranai.

Mein XING Profil.

4.506 Beiträge seit 2004
vor 16 Jahren

Hallo talla,

danke für Deine Antwort. Ja ich habe geschrieben, dass ich das im Bezug zu automatischen Eigenschaften meinte.

Sprich das Feature nutzt nur was wenn man wirklich nur stur Variablen durch Properties veröffentlichen will

Das würde für mich nun konkret bedeuten, dass das A) anders herum funktioniert wie ich es implementieren würde, und B) dass es für mich nicht anwendbar ist, weil ich eben die privaten Felder besonders markieren möchte, was die Automatik ja nicht macht.

Zu A) -> Ich entwickle normalerweise Klassen so, dass ich zunächst alles definiere was ich für die Klasse benötige. Erst danach überlege ich mir, was ich öffentlich machen möchte...

Aber für andere mag das ja dann Sinn machen.

Vielen Dank,
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

6.862 Beiträge seit 2003
vor 16 Jahren

Hallo,

oh ja, mein Fehler 😠 Hatte wohl mal wieder meine 5 blinden Minuen gg

Erstmal zu B): Du hast ja dann gar keine expliziten privaten Felder mehr. Wenn man aber dann halt private Felder mit diesen automatischen Properties mischt, sprich man muss einige Properties selber in der Klasse setzen, andere Werte setzt man in privaten Feldern, dann wirkt das schon ein wenig inkosistent.

Wo ich mir die aber gut vorstellen kann sind Code Generatoren, wenn z.B. irgendwelche komplexen Datenklassen erstellt werden, da verkürzt sich der Code erheblich wenn nicht immer explizit nen privates Feld angelegt werden muss.

Zu A): Da kommt ja der Gedanke heraus - ich muss ja eh ein privates Feld für nen Property erstellen - also erstmal alles privat erstellen 🙂 Weiß nicht, vielleicht ist das Gewöhnungssache ob man nicht doch gleich Properties schreibt. Wenn ich weiß das meine Klasse bestimmte Properties zur Verfügung stellen soll, dann mach ich die meistens gleich als Properties - von daher kommen die automatischen Properties mit ein wenig entgegen, was aber auch nicht heißt das ich sie laufend verwenden werde, halt mal sehen was sich so anbietet.

Baka wa shinanakya naoranai.

Mein XING Profil.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo zusammen,

was wohl ein guter Abschluss der Diskussion an dieser Stelle ist. Wenn einzelne Sprachfeaures ausführlicher besprochen werden sollen, als man das in 4-5 Beiträgen machen kann, öffnet bitte einen neuen Thread. Vielen Dank!

herbivore

E
95 Beiträge seit 2006
vor 16 Jahren

Hallo,

ohne die Diskussion neu anheizen zu wollen würde ich dennoch die Frage von feadur gerne beantworten.

Man kann auch schreiben:

public string FirstName { get; internal set; }

Gruß Thorsten

U
239 Beiträge seit 2006
vor 16 Jahren

Automatische Readonly-Properties kann man so schreiben (hab ich gerade in der MSDN-Hilfe gefunden):

public IQueryProvider Provider { get; private set; }