C# version 5
Asynchronous members
See Asynchronous programming with the Task Based Asynchronous Pattern
You will come to know and love definitely not fear:
- The concepts of "I/O bound" versus "CPU-bound code"
- The classes
Task
andTask<T>
. - The keywords
await
andasync
.
For I/O-bound code, you
await
an operation which returns aTask
orTask<T>
inside of an async method
and
For CPU-bound code, you
await
an operation which is started on a background thread with theTask.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));
}
- Shared online with linqpad query sharing.... -- includes version 6 features
This would've avoided a few NT1
errors back in T-S
days.