DotNetProgramming
This page contains an assortment of notes regarding programming on the Microsoft .NET platform. I hope to expand this at a later time and add a better structure to this and other pages.
GAC installer
On machines with a Visual Studio development environment the command line utility gacutil.exe
exists to install assemblies into the GAC (global assembly cache), or remove them from the GAC. Unfortunately, gacutil.exe
cannot be copied to other machines where Visual Studio does not exist - the utility just doesn't seem to work there.
The following untested code is supposed to compile into an executable that can be used as a replacement for gacutil.exe
on non-devenv machines.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.EnterpriseServices.Internal; using System.IO; namespace GacTool { class Program { static void Main(string[] args) { Console.Write("\nEnter full path of assembly file:"); Console.Write("\n> "); string path = Console.ReadLine(); if (File.Exists(path)) { Console.WriteLine("\nInstalling assembly to GAC ..."); Publish publish = new Publish(); publish.GacInstall(path); } else { Console.Write("\nAssembly file does not exist."); } Console.Write("\nPress any key to exit ... "); Console.ReadLine(); } } }
Asynchronous programming
Keywords: TAP, TPL, async, await
References
- Task-based asynchronous pattern (TAP). This article provides a good high-level overview of the
Task
class, various aspects of asynchronous programming (e.g. exceptions, return values) and some coding guidelines both when you implement an asynchronous operation and when you call a method that implements an asynchronous operation. - Asynchronous programming
- ConfigureAwait FAQ: An excellent article that explains in detail how
ConfigureAwait
works, and also gives some general advice when to use it.
Glossary
- APM
- Asynchronous Programming Model
- EAP
- Event-based Asynchronous Pattern
- TAP
- Task-based asynchronous pattern
The Task class
The Task
class represents an asynchronous operation.
A method that launches an asynchronous operation returns
Task
if the operation return value isvoid
Task<TResult>
if the operation return value has the typeTResult
async/await
Using async/await is called the "Task Based Asynchronous Pattern".
Task<int> Foo { Thread.Sleep(2000); return 42; } async void Bar() { // The await operator suspends Bar(). // - Bar can't continue until Foo() is complete. // - Meanwhile, control returns to the caller of Bar(). // - Control resumes here when Foo() is complete. // - The await operator then retrieves the integer result from Foo(). // - The resumed code executes on the original thread because we didn't // use ConfigureAwait(false) - see next section on this wiki page. int result = await Foo(); Baz(result); } void Baz(int i) { Console.WriteLine("i = {0}"); } void DoIt() { Bar(); Baz(17); } Result: i = 17 i = 42
Notes:
- In order to be able to use the
await
keyword you must mark the method where it is used withasync
ConfigureAwait
When we see this code, what thread does Baz(result)
run on?
int result = await Foo(); Baz(result);
The options are:
- The same thread that await'ed Foo()
- A different thread
ConfigureAwait()
is used to control our options, at least to a certain degree. The parameter value passed to ConfigureAwait()
is the deciding factor:
ConfigureAwait(true)
: Guarantees that the code afterawait
runs in the same thread as the code beforeawait
. For this to happen the .NET framework must take certain actions that involveSystem.Threading.SynchronizationContext
andSystem.Threading.Tasks.TaskScheduler
. For details read the "ConfigureAwait FAQ" article listed in the references section.ConfigureAwait(false)
: Tells the .NET framework to not take those special actions mentioned forConfigureAwait(true)
. This means that the code afterawait
might run in the same thread as the code beforeawait
(if the await'ed task is already complete), but it probably will run in a different thread. There are two types of advantage that can be gained from usingConfigureAwait(false)
: Performance (because the .NET framework does not have to take special synchronization actions), and reliability (because there is no risk from deadlocks). For details read the "ConfigureAwait FAQ" article listed in the references section.
The default behaviour if we don't write ConfigureAwait()
at all is the same as if we had written ConfigureAwait(true)
. Anytime a piece of code contains the call ConfigureAwait(true)
, this could be deleted without affecting the functionality. You may want to keep the call to explicitly show an intent.
This means that actually only ConfigureAwait(false)
matters. So when to use it? Here I'm replicating the general advice from the "ConfigureAwait FAQ" article in a very condensed form. You should really read and understand the article before following this condensed advice:
- When writing application-level code, don't use
ConfigureAwait(false)
. The reason is that you don't want to subvert a customSynchronizationContext
that might be in place in your application environment (e.g. Windows Forms, WPF, ASP.NET Core, etc.). - When writing general-purpose library code you should actively use
ConfigureAwait(false)
, because the library does not interact with the application environment and therefore does not have to respect any potential customSynchronizationContext
that is in effect for that environment. - Exception: When the general-purpose library has APIs that take delegates to be invoked, don't use
ConfigureAwait(false)
. The reason is that the caller of the library is passing potentially app-level code to be invoked by the library, which then effectively renders those “general purpose” assumptions of the library moot.
BeginInvoke/EndInvoke
Using a delegate's BeginInvoke()
and EndInvoke()
methods is called the "asynchronous pattern" or the "asynchronous component pattern".
Note: The following example mixes two different things: Working with a callback and synchronously waiting with EndInvoke. This example is for demonstration only, normally you would do either one or the other.
public class Foo { public string Sleep(int numberOfSecondsToSleep, out double result) { Console.WriteLine("Sleeping ..."); Thread.Sleep(numberOfSecondsToSleep * 1000); result = 42.0; return "Finished sleeping"; } } // The delegate must have the same signature as the method it will call asynchronously public delegate string AsyncMethodCaller(int numberOfSecondsToSleep, out double result); // The callback method must have the same signature as the AsyncCallback delegate. void FooSleepCallback(IAsyncResult ar) { AsyncResult asyncResult = (AsyncResult)ar; AsyncMethodCaller asyncMethodCaller = (AsyncMethodCaller)result.AsyncDelegate; string result; string returnValue = asyncMethodCaller.EndInvoke(out result, ar); string stateInformationPassedToCallback = (string) ar.AsyncState; Console.WriteLine("callback: result = {0}, returnValue = {1}, state = {2}", result, returnValue, stateInformationPassedToCallback ); } void DoIt() { Foo foo = new Foo(); AsyncMethodCaller asyncMethodCaller = new AsyncMethodCaller(foo.Sleep); AsyncCallback asyncCallback = new AsyncCallback(FooSleepCallback); object stateInformationPassedToCallback = "callback state info"; int numberOfSecondsToSleep = 5; string result; IAsyncResult asyncResult = asyncMethodCaller.BeginInvoke( numberOfSecondsToSleep, // This is a dummy out parameter - it won't contain anything useful when BeginInvoke() returns out result, // The following two parameters can be null if you don't want to work with callbacks asyncCallback, stateInformationPassedToCallback); Console.WriteLine("Doing something parallel while method is invoked asynchronously"); // Blocks the curent thread and waits for the asynchronous call to complete string returnValue = asyncMethodCaller.EndInvoke( // Unlike with BeginInvoke(), this time the out parameter will actually be filled with a value when EndInvoke() returns out result, asyncResult); Console.WriteLine("main method: result = {0}, returnValue = {1}", result, returnValue); }