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
...