articles » current » dot-net » c-sharp-7

.NET: What's New or Changed in C# 7

A guide to the changes and additions in C# version 7

Improvements to C# are discussed at https://github.com/dotnet/csharplang. Here, you can find proposals to the language and submit your own.

async Main

In C# 7.1, the main method can be a Task using await and async.

class Program
{
	async static System.Threading.Tasks.Task Main()
	{
		await AsyncMethod();
	}
}

Expresssion-Bodied Members

Constructors, Destructors and accessors can be expresson-bodied members.

class Business
{
	private string _name;

	// expression-bodies get & set accessors
	public string Name { get => _name; set => _name = value ?? ""; }

	// expression-bodied constructor
	public Business(string name) => Name = name;

	// expression-bodied destructor
	~Business() => Console.WriteLine("~");
}
class Program
{
	static void Main(string[] args)
	{
		Business bus = new Business(null);
		Console.WriteLine(bus.Name);
	}
}

New out Variables

out variables can be declared on use.

string s = "123";
if(int.TryParse(s, out int num))
{
	// do something with num
	num++;
}

New in Parameters

In C# 7.2, in function parameters cannot be modified and, like ref, are passed by reference thereby avoiding a copy operation.

string str = "abc";
void DoSomething(in string s)
{
	// s can be accessed but not modified
}

Tuples

Tuples allow the combining of objects of different types. Previously, tuples required the use of the Tuple class. Now, in C# 7, tuples are part of the language. In C# 7.1, tuples are further extended by automatically inferring the tuple name.

var tup1 = (str: "this is a string", num: 123);
int num = tup1.num;
string str = tup1.str;
var tup2 = (BusinessName: obj.BusinessName, Price: obj.Price);
double price = tup2.Price;
// in C# 7.1, tuples automatically infer the tuple name
var tup2 = (obj.BusinessName, obj.Price);
double price = tup2.Price;

As of C# 7.3, tuples now support == and !=. These comparisons short-circuit meaning that they stop comparing when further evaluation is not important or does not affect the outcome. Read more about short-circuiting under operators.

Deconstructors

Tuples can be deconstructed (as well as user defined objects).

(string name, int age) = ("Jane", 35);

Non-trailing Named Arguments

In C# 7.2, arguments need not be named when trailing named arguments.

string str = "10";
if(int.TryParse(s: str, out int num)) // this will not generate an error in C# 7.2
{
	num *= 100;
}

readonly struct

In C# 7.2, it is possible to declare a struct as readonly so that the compiler makes sure that the struct goes unchanged.

public readonly struct RightTriangle
{
	public double leg1 { get; }
	public double leg2 { get; }

	public RightTriangle(double leg1, double leg2)
	{
		this.leg1 = leg1;
		this.leg2 = leg2;
	}

	public double Hypontenuse() => Math.Sqrt(leg1 * leg1 + leg2 * leg2);
}

private protected

private protected allows access only if the class using these keywords derives from the base class and is in the same assembly.

// Inside the same assembly
namespace ConsoleApp1
{
	public class MyClass
	{
		protected private void GetPrivateProtected() { }
	}
	class YourClass : MyClass
	{
		MyClass mc = new MyClass();
		public void Show()
		{
			GetPrivateProtected(); // OK
		}
	}
}
//Outside the assembly
using ConsoleApp1;
namespace ClassLibrary1
{
	public class YourClass : MyClass
	{
		MyClass mc = new MyClass();
		public void show()
		{
			//GetPrivateProtected(); Not accessible as Private Protected members are not accessible outside of assembly by creating an object or through inheritance.
		}
	}
}

Target-Typed default

In C# 7.1, a default literal is now defined to help save typing time when the type has a long name.

System.Collections.Generic.IEnumerable<string> iestr = default;

Local Functions

A local function can be declared within a method. This is not a lambda expression.

public void SomeMethod()
{
	int multiply(int a, int b) => a * b; // a local function

	int x = multiply(3, 4);
}

Pattern Matching with switch and is

switch and is are enhanced with the var pattern, the type pattern and the const pattern.

object obj = null;
if(obj is Square s)
{
	s.Length = 100;
}
if (obj is 30)
{
	;
}
if(obj is var v)
{
	Console.WriteLine(v);
}
switch(obj)
{
	case 33:
		break;
	case Square sq when sq.Length > 0:
		sq.Length *= 2;
		break;
	case Square sq:
		sq.Length = 40;
		break;
	case var va:
		Console.WriteLine(va);
		break;
}

ref Locals and Returns

Now it's possible to use ref with local variables and the return type. A ref local is basically a pointer.

class Temp
{
	private int[] nums = { 0, 8, 4, 12, 1, 5 };
	private ref int GetPointerToNumber(int i)
	{
		return ref nums[i];
	}
	public void Run()
	{
		ref var idx3 = ref GetPointerToNumber(3);
		idx3 <<= 1; // multiply by 2, nums[3] == 24
	}
}

In C# 7.2, using readonly, the caller gets a reference to the data but is not allows to change it.

class Temp
{
	private int[] nums = { 0, 8, 4, 12, 1, 5 };
	private ref readonly int GetPointerToNumber(int i)
	{
		return ref nums[i];
	}
	public void Run()
	{
		ref readonly var idx3 = ref GetPointerToNumber(3);
idx3 <<= 1; // cannot do this, idx3 is readonly
	}

}

Digit Separators

The underscore (_) serves as a digit separator.

// now there are digit separators to help make these and other number literals more readable
uint hex = 0x_FF_1E_23_56

Binary Literals

Totally new are binary literals.

uint bin = 0b1010000100000001
// now with digit separators
uint bin2 = 0b_10100001_00000001

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE