You will need Visual Studio 2017 or higher. (Released March 2017). .NET Framework. 4.7.
out variables can now be declared in the caller argument list.
// old syntax
private void Test1()
{
// out variable must be declared prior to use
int age;
GetAge(out age);
Console.WriteLine($"Your age is {age}");
}
// new syntax
private void Test1()
{
// out variable can be declared in the argument list
GetAge(out int age);
Console.WriteLine($"Your age is {age}");
}
private void GetAge(out int age)
{
age = 20;
}
Tuples now have full language support. No need to use System.Tuple.
private void PrintWeather()
{
var w = GetWeather();
Console.WriteLine("{0}, high temp {1}, low temp {2}", w.Item1, w.Item2, w.Item3);
}
// This is the new tuple - System.ValueTuple
private (string, int, int) GetWeather()
{
var w = ("Rain", 74, 55);
return w;
}
// This is the old tuple - using Sytem.Tuple
private Tuple<string,int,int> GetWeather()
{
var w = Tuple.Create("Rain", 74, 55);
return w;
}
private void PrintWeather()
{
// Using named fields
var w = GetWeather();
Console.WriteLine("{0}, high temp {1}, low temp {2}", w.Forecast, w.High, w.Low);
// deconstructing the tuple
(string weather, int high, int low) = GetWeather();
Console.WriteLine("{0}, high temp {1}, low temp {2}", weather, high, low);
}
// Using named fields
private (string Forecast, int High, int Low) GetWeather()
{
var w = (Forecast: "Rain", High: 74, Low: 55);
// or
w = ("Rain", 74, 55);
return w;
}
3a) is expression with pattern variables
Pre C# 7, you would need to check for type, declare a variable, and then cast to assign.
public static int SumValues(IEnumerable<object> values)
{
var sum = 0;
foreach(var item in values)
{
if (item is int)
{
var val = (int)item;
sum += val;
}
}
return sum;
}
The new C# 7 syntax lets you check, declare, and assign, in one line.
public static int SumValues(IEnumerable<object> values)
{
var sum = 0;
foreach(var item in values)
{
if (item is int val)
sum += val;
}
return sum;
}
3b) switch statement with pattern variable
You can now switch on an object type, or type with condition.
public void TestObject(object obj)
{
switch(obj)
{
case System.Drawing.Point p:
break;
case System.Drawing.Rectangle r when (r.IsEmpty):
break;
case System.Drawing.Rectangle r:
break;
}
}
Prior to C# 7.0, you would need to write:
public void TestObject(object obj)
{
if(obj is Point)
{
var p = (Point)obj;
}
else if(obj is Rectangle)
{
var r = (Rectangle)obj;
}
}
Lets you return a reference to a value type. Similar to the C++ operator & (address of).
static void Main()
{
var m = new Math();
// this is a ref local
ref double pi = ref m.GetPi();
pi = 3; // change PI
pi = m.GetPi(); // re-get PI
Console.WriteLine("PI is {0}", pi);
}
class Math
{
private double _pi = 3.14159265359;
public ref double GetPi()
{
// this is a ref return
return ref _pi;
}
}
PI is 3
In this example, we were able to change the value of the double returned by Math.GetPi() because we returned the value by reference.
void Main()
{
void Log(string message) { Console.WriteLine("Main() - {0}", message); }
Log("begin");
Log("end");
}
Test1() - begin Test1() - end
C# 6 introduced expression-bodied members.
public override ToString() => "To be or not to be.";
class MyClass1
{
int _count;
// constructor
public MyClass1(int count) => _count = count;
// finalizer
~MyClass1() => Console.WriteLine("finalizer called");
// get/set accessors
public int Count
{
get => _count;
set => _count = value;
}
}
Since throw is a statement, and not an expression, it can't be used in the middle of an expression. Example below:
void throw_expression()
{
var c = new MyClass1();
c.Message = "Hello";
}
class MyClass1
{
string _message;
public string Message
{
get { return _message; }
set { _message = value==null ? throw new ArgumentNullException("Message") : _message = value; }
}
}
Under C# 6, this fails with the error:
Invalid expression term 'throw'
In C# 7, throw is an expression, and the above syntax is valid.
Any method declared as async, must have one of the following return types:
C# 7 allows a value type to be returned using the ValueTask type.
public async ValueTask<int> DoSomethingAsync()
{
return 1;
}
ValueTask<TResult> is recommended over Task<TResult> when the task is expected to complete synchronously and be called frequently.
Support for binary syntax and digit separators.
// prefix with 0b for binary
const int BitMask3 = 0b00000011;
// use underscore as a digit separator
const long OneHundredBillion = 100_000_000_000; // More readable than 100000000000