DotNetProgramming

From HerzbubeWiki
Jump to navigation Jump to search

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 is void
  • Task<TResult> if the operation return value has the type TResult


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 with async


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 after await runs in the same thread as the code before await. For this to happen the .NET framework must take certain actions that involve System.Threading.SynchronizationContext and System.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 for ConfigureAwait(true). This means that the code after await might run in the same thread as the code before await (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 using ConfigureAwait(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 custom SynchronizationContext 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 custom SynchronizationContext 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".

Reference: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously

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);
}