C# version 5

See what's new in C#5

Asynchronous members

See Asynchronous programming with the Task Based Asynchronous Pattern

You will come to know and love definitely not fear:

For I/O-bound code, you await an operation which returns a Task or Task<T> inside of an async method

and

For CPU-bound code, you await an operation which is started on a background thread with the Task.Run method.

CPU-Bound example

Imagine we have a very expensive CPU-bound calculation, such as:

private int CalculateFoo()
{
	Thread.Sleep(3000);
	return 1;
}

(Ideally it would be doing something intensely cool and mathematical instead of just sleeping. This is just a tribute to such code...)

How can we ensure our code stays responsive, even while we do such an intense calculation?

(This is an example designed for linqPad...)

void Main()
{
	var downloadButton = new Button() { Text = "Think Hard", Dock = DockStyle.Fill};

	downloadButton.Click += async (o, e) =>
	{
		((Control)o).Text = "About to Think:";
		((Control)o).Enabled = false;

		var t = Task.Run(() => CalculateFoo());

		((Control)o).Text = "Thinking.....";
		var i = await t;

		((Control)o).Text = "The answer is " + i;
		((Control)o).Enabled = true;
	};

	using(var f = new Form()) {
		f.Controls.Add(downloadButton);
		f.ShowDialog();
	}
}

I/O Bound example

Instead of doing something on our local CPU, perhaps we need to do something on someone else's machine (e.g. "in the cloud") or in a database, or on a disk, or by giving a printer some instructions to ignore.

In such cases we are no longer CPU bound but I/O bound.

How would that be done?

void Main()
{
	var downloadButton = new Button() { Text = "Think Hard", Dock = DockStyle.Fill};

	downloadButton.Click += async (o, e) =>
	{
		((Control)o).Text = "About to Think:";
		((Control)o).Enabled = false;

		var stringData = await _httpClient.GetStringAsync(url);

		// Do something with our data...
		Console.WriteLine(stringData);

		((Control)o).Text = "Downloaded.";
		((Control)o).Enabled = true;
	};

	using(var f = new Form())
	{
		f.Controls.Add(downloadButton);
		f.ShowDialog();
	}
}

If the 'Do something with our data' was going to be a CPU-intensive operation... then we'd use the technique in the first example to handle it.

Caller info attributes

Well look at this!

By applying these "Caller" related attributes from System.Runtime.CompilerServices to some members you can have them magically populated with some info from the compiler....

This is a strange magic!

// using System.Runtime.CompilerServices;

void Main()
{
	LogThisMomentInTime("Here I am!");
}

void LogThisMomentInTime(string message,
		[CallerMemberName] string memberName = "",
		[CallerFilePath] string sourceFilePath = "",
		[CallerLineNumber] int sourceLineNumber = 0)
{
	message.Dump("Message");
	memberName.Dump("MemberName");
	sourceFilePath.Dump("SourceFilePath");
	sourceLineNumber.Dump("SourceLineNumber");
}

Bonus -- we can also add on top the version 6 feature, nameof, and even get rid of the hard-coded "Message" string and others above!

//using System.Runtime.CompilerServices;

void LogThisMomentInTime(string message,
		[CallerMemberName] string memberName = "",
		[CallerFilePath] string sourceFilePath = "",
		[CallerLineNumber] int sourceLineNumber = 0)
{
	// Look ma, no hard coded strings!!
	message.Dump(nameof(message));
	memberName.Dump(nameof(memberName));
	sourceFilePath.Dump(nameof(sourceFilePath));
	sourceLineNumber.Dump(nameof(sourceLineNumber));
}

This would've avoided a few NT1 errors back in T-S days.

Sources

See also