Allgemein Für Administratoren Für Architekten Für Entwickler Für Projektleiter Für Tester News Produkte Publikationen
X
Florian Bader

Florian Bader

Neu in Visual Studio und TFS "15" / 2017: C# 7 – Teil 1

Montag, 16. Januar 2017

Mit der Offenlegung des C#-Compilers können neue C#-Features bereits heute verwendet werden. Da dieser Prozess jedoch aufwändig und nicht unbedingt geeignet für ein produktives Umfeld ist, gibt es mit C# 7 wieder ein Abbild des aktuellen Stands. Dabei wird C# 7 zusammen mit Visual Studio 2017 ausgerollt. Da eine Beschreibung aller Features von C# 7 den Rahmen eines Blog Posts sprengen würde, gibt es hier den ersten Teil.

Binary literals

Mit Binary literals wird eine Möglichkeit geboten, um Binärzahlen im Code besser darzustellen. Dies ist vor allem nützlich für jeden, der hardwarenah in C# programmiert.

Beispiel

In diesem Beispiel soll die Zahl 8 als Binärformat dargestellt werden, zum einen über die neue Technik und zum anderen im Vergleich, wie es bereits heute funktioniert. In jedem der dargestellten Fälle wird am Ende in der Variablen EnableFlag die Zahl 8 stehen.

// Integer representation
public const int EnableFlag = 8;

// Shift representation
public const int EnableFlag = 1 << 3;

// Convert representation
public const int EnableFlag = Convert.ToInt32("1000", 2);

// Binary literal representation (C#7)
public const int EnableFlag = 0b1000;

Digit separators

Mit der neuen Binärdarstellung geht ein weiteres Problem einher. Was, wenn längere Binärzahlen oder z.B. große Dezimalzahlen dargestellt werden sollen. Ein Trennzeichen z.B. für Tausenderstellen oder Halbbytes, um die Zahlen besser lesbar zu machen, würde die Lesbarkeit verbessern. Auch hier bietet C# 7 eine interessante Neuerung.

Das Trennzeichen wurde auf den Unterstrich _ festgelegt. Mit diesem ist es möglich, beliebige Zahlenwerte zu trennen. Dabei kann es beliebig oft wiederholt werden (siehe Beispiel SpecialNumber) und wird später beim Kompilieren einfach ignoriert. Zu Bedenken gilt: Das Trennzeichen kann nur zwischen Ziffern stehen und nicht zwischen Sonderzeichen (z.B. der Dezimalpunkt oder am Anfang oder am Ende einer Zahl).

Beispiel

public const int EnableFlag = 0b0000_1000;
public const decimal IntervalInMilliseconds = 1_000_000.500_50;
public const int WhiteColor = 0xFF_FF_FF;
public const decimal SpecialNumber = 10___543__245_123.500_241;

Ref locals

Vielen C#-Entwicklern dürfte ref von Methoden-Parametern bekannt vorkommen. Durch dieses Keyword können Parameter als Referenz statt als Wert an eine Methode übergeben werden.

Ref locals ermöglicht es nun, eine Referenz auf eine Variable zu deklarieren. Wird die lokale Referenz angepasst, so verändert sich damit auch der Ursprungswert. Ref locals ergeben alleine wenig Sinn, mit dem nächsten Feature in der Liste (ref returns) wird der Anwendungsfall hierfür deutlich klarer.

Beispiel

int var1 = 5;
ref int refVar = ref var1;
refVar = 15; // var = 15

int var2 = 5;
var refVar2 = var2;
refVar2 = 15; // var2 = 5;

Ref returns

Eine Referenz als Parameter übergeben gibt es bereits, was noch fehlt ist als Rückgabewert einer Funktion eine Referenz statt eines Werts zu definieren. Dieses fehlende Feature wird nun auch in C# 7 abgedeckt.

Als Vorteil ergibt sich, dass der Rückgabe nicht kopiert werden muss, sondern eine Referenz zurückgegeben wird. Besonders bei großen Strukturen ergibt sich hier ein großer Performance- und Speichervorteil. Zusätzlich kann der Rückgabewert bearbeitet werden, wodurch sich der Ursprungswert verändert.

Beispiel

ref int Max(ref int x, ref int y)
{
   if (x > y)
      return ref x;
   else
      return ref y;
}

int a = 5;
int b = 10;
ref int max = ref Max(ref a, ref b);
max = 0; // b = 0

Local functions

Lokale Funktionen bieten die Möglichkeit, innerhalb einer Funktion eine weitere Funktion zu definieren. Was heute schon über die Func- und Action-Klassen möglich ist, kann nun auch direkt als lokale Funktion deklariert werden.

Durch die Nutzung von lokalen Funktionen ergeben sich mehrere Vorteile:

  1. ref und out Parameter können nun in lokalen Funktionen verwendet werden
  2. Beim Anlegen einer Action oder Func wird ein Objekt auf dem Heap allokiert. Dies passiert bei einer lokalen Funktion nicht. Diese wird durch den Compiler entweder in eine statische oder in eine Klassen-Methode umgewandelt, je nachdem ob Konstrukte der Klasse verwendet wurden.
  3. Werden Variablen außerhalb der Funktion verwendet (im Beispiel wäre das offsetX), so generiert der C#-Compiler im Falle einer Func oder Action eine separate Klasse mit einem Feld pro verwendeter Variablen. Bei einer lokalen Funktion wird jedoch ein struct, also ein Value Type, generiert. Dadurch wird kein Speicher auf dem Heap benötigt und es gibt auch keine Bedenken bei parallelem Zugriff.

Beispiel

int Compute()
{
   int offsetX = 5;

   var addOffset = new Func<int, int>(x => x + offsetX);
   addOffset(5); // 10

   int AddOffset(int x) => x + offsetX;
   AddOffset(5); // 10
} 

Tuple

Wer mehr als einen Rückgabewert benötigt oder eine Sammlung an Daten erstellen wollte, die zu kurzlebig für eine eigene Klasse sind, musste entweder auf out-Parameter oder eine Tuple-Klasse zurückgreifen.

Die Tuple-Klasse gibt es seit dem .NET Framework 4. In dieser kann eine beliebige Kombination an Daten abgelegt werden. Das Problem an der Tuple-Klasse ist, dass die zugewiesenen Properties keine sprechenden Namen haben. Diese werden mit Item1 bis ItemN abgerufen.

Das Tuple-Feature in C# 7 ermöglicht es nun, benannte Tuples zu erstellen und führt hierzu sogar eine eigenen Syntax ein.

Zu Bedenken gibt es hier nur, dass Tuple kurzlebig und klein sein sollten, da die Syntax den Code doch recht schnell unleserlich macht. Alles andere sollte in eine eigene Klasse oder Struktur ausgelagert werden.

Beispiel

(int count, decimal sum) Compute(IEnumerable<int> items)
{
   var result = new (int count, decimal sum) { count = 0, sum = 0 };
   foreach (var item in items)
   {
      result.count++;
      result.sum += item;
   }
}

(int c, decimal s) x = Compute(new[] { 1, 2, 3, 4, 5 });
Console.WriteLine($"count = {c}, sum = {x.s}"); // count = 5, sum = 15

Out Parameter

Wer eine Funktion mit Out-Parametern verwendet (das beste Beispiel ist die TryParse-Methode), der musste sich zuerst mit einer Variablendeklaration herumschlagen, bevor man die Funktion verwenden konnte. Doch C# 7 bietet nun Abhilfe, indem man diese Variablendeklaration direkt mit dem Out-Parameter verbinden kann.

Beispiel

if (int.TryParse("500", out int cost))
   Console.WriteLine(cost); // 500

Persönlich freue ich mich bereits auf Features wie Local Functions, Tuples und Out Parameter, da der Anwendungsfall hierfür doch recht häufig vorkommt. Die anderen Features sind zwar nicht unwichtig, hier ist die Häufigkeit der Anwendung aber eher gering.

Im nächsten Teil soll auf die weiteren Feature wie Pattern Matching, Throw Expressions, Expression bodied members und Generalized async return types eingegangen werden. Da sich Visual Studio zum Zeitpunkt des Blogpost noch in der RC-Version befindet, können unter Umständen bestimmte C# 7 Features nicht in der finalen Version enthalten sein.

Verwandte Artikel:

Benötigen Sie Unterstützung bei der Software-Entwicklung und Architektur von .NET basierten Lösungen oder bei Einführung und Anpassung von Visual Studio / Microsoft Test Manager / Team Foundation Server?

Wir stehen Ihnen unter info(at)aitgmbh.de gerne zur Verfügung.

Tags: , , ,

Hinterlasse eine Antwort