Code Listings

Chapter 24: Application domains

Using DoCallback:

class Program
{
  static void Main ()
  {
    AppDomain newDomain = AppDomain.CreateDomain ("New Domain");
    newDomain.DoCallBack (new CrossAppDomainDelegate (SayHello));
    AppDomain.Unload (newDomain);
  }

  static void SayHello()
  {
    Console.WriteLine ("Hi from " + AppDomain.CurrentDomain.FriendlyName);
  }
}

Isolating unit tests with application domains:

class Program
{
  static void Main()
  { 
    // Create 20 domains and 20 threads.
    AppDomain[] domains = new AppDomain [20];
    Thread[] threads = new Thread [20];

    for (int i = 0; i < 20; i++)
    {
      domains [i] = AppDomain.CreateDomain ("Client Login " + i);
      threads [i] = new Thread (LoginOtherDomain);
    }

    // Start all the threads, passing to each thread its app domain.
    for (int i = 0; i < 20; i++) threads [i].Start (domains [i]);

    // Wait for the threads to finish
    for (int i = 0; i < 20; i++) threads [i].Join ();

    // Unload the app domains
    for (int i = 0; i < 20; i++) AppDomain.Unload (domains [i]);
    Console.ReadLine ();
  }

  // Parameterized thread start – taking the domain on which to run.
  static void LoginOtherDomain (object domain)
  {
    ((AppDomain) domain).DoCallBack (Login);
  }

  static void Login()
  {
    Client.Login ("Joe", "");
    Console.WriteLine ("Logged in as: " + Client.CurrentUser + " on " +
      AppDomain.CurrentDomain.FriendlyName); 
  }
}

class Client
{
  // Here's a static field that would interfere with other client logins
  // if running in the same app domain.
  public static string CurrentUser = "";

  public static void Login (string name, string password)
  {
    if (CurrentUser.Length == 0)    // If we're not already logged in...
    {
      // Sleep to simulate authentication...
      Thread.Sleep (500);
      CurrentUser = name;           // Record that we're authenticated.
    }
  }
}

Sharing data via slots:

class Program
{
  static void Main()
  {
    AppDomain newDomain = AppDomain.CreateDomain ("New Domain");

    // Write to a named slot called "Message" – any string key will do.
    newDomain.SetData ("Message", "guess what...");

    newDomain.DoCallBack (SayMessage);
    AppDomain.Unload (newDomain);
  }

  static void SayMessage()
  {
    // Read from the "Message" data slot
    Console.WriteLine (AppDomain.CurrentDomain.GetData ("Message"));
  }
}

Intra-process remoting:

class Program 
{
  static void Main()
  {
    AppDomain newDomain = AppDomain.CreateDomain ("New Domain");

    Foo foo = (Foo) newDomain.CreateInstanceAndUnwrap (
                      typeof (Foo).Assembly.FullName,
                      typeof (Foo).FullName); 
    
    Console.WriteLine (foo.SayHello());
    AppDomain.Unload (newDomain);
    Console.ReadLine();
  }
}

public class Foo : MarshalByRefObject
{
  public string SayHello()
  {
    return "Hello from " + AppDomain.CurrentDomain.FriendlyName;
  }

  public override object InitializeLifetimeService()
  {
    // This ensures the object lasts for as long as the client wants it
    return null;
  }
}

A simple plug-in architecture:

namespace Plugin.Common
{
  public interface ITextPlugin
  {
    string TransformText (string input);
  }
}
namespace Plugin.Extensions
{
  public class AllCapitals : MarshalByRefObject, Plugin.Common.ITextPlugin
  {
    public string TransformText (string input) { return input.ToUpper(); }
  }
}
using System;
using System.Reflection;
using Plugin.Common;

class Program 
{
  static void Main()
  {
    AppDomain domain = AppDomain.CreateDomain ("Plugin Domain");

    ITextPlugin plugin = (ITextPlugin) domain.CreateInstanceFromAndUnwrap
      ("AllCapitals.dll", "Plugin.Extensions.AllCapitals");

    // Call the TransformText method using Remoting:
    Console.WriteLine (plugin.TransformText ("hello"));   // "HELLO"

    AppDomain.Unload (domain);

    // The AllCapitals.dll file is now completely unloaded and could
    // be moved or deleted.
  }
}

Type discovery:

public class Discoverer : MarshalByRefObject
{
  public string[] GetPluginTypeNames (string assemblyPath)
  {
    List<string> typeNames = new List<string> ();
    Assembly a = Assembly.LoadFrom (assemblyPath);
    foreach (Type t in a.GetTypes ())
    if (t.IsPublic
        && t.IsMarshalByRef 
        && typeof (ITextPlugin).IsAssignableFrom (t))
    {
      typeNames.Add (t.FullName);
    }
    return typeNames.ToArray ();
  }
}
class Program 
{
  static void Main()
  {
    AppDomain domain = AppDomain.CreateDomain ("Plugin Domain");

    Discoverer d = (Discoverer) domain.CreateInstanceAndUnwrap (
      typeof (Discoverer).Assembly.FullName,
      typeof (Discoverer).FullName);

    string[] plugInTypeNames = d.GetPluginTypeNames ("AllCapitals.dll");

    foreach (string s in plugInTypeNames)
      Console.WriteLine (s);              // Plugin.Extensions.AllCapitals

    ...
C# 12 in a Nutshell
Buy from amazon.com Buy print or Kindle edition
Buy from ebooks.com Buy PDF edition
Buy from O'Reilly Read via O'Reilly subscription