Jason Bock

Home Biography Speaking Articles Books Music

Passing Methods Information

The title of this post may sound a little odd, but here’s the situation we’ve run into. We’re incorporating the singleton pattern in our application to manage file watching. It’s actually a nice, slick design - the end result is we’re doing something like this:

SpecificData data = SpecificData.Instance;

This works well because the file that SpecificData watches is named in a configuration file. But this pattern is limiting because we can’t pass in the name of the file. Instance is a property so we can’t define arguments. Even if we change it to a method like GetInstance(), it still won’t work because the structure of our implementation requires the same signature.

There’s a way around this - I’ll generalize it using the creator pattern:

public static class Loader
{
  public static ILoaded Load()
  {
    return new Load();
  }
}

It’s a simple implementation, but it shows the issue. Load() returns an implementation of ILoaded:

public interface ILoaded
{
  string Unload();
}

But the creational method takes no arguments. Therefore, we can’t get any information to Load:

public sealed class Load : ILoaded
{
  private string stuff;
    
  public Load()
    : base()
  {
  }

  public string Unload()
  {
    return this.stuff;
  }
}

What happens if we want to get stuff set to a value, other than by reading a configuration file? There’s a way to do it via CallContext and defining a class that implements ILogicalThreadAffinative:

[Serializable]
public sealed class ExtraStuff : ILogicalThreadAffinative
{
  public const string CallContextId = "ExtraStuff";

  private string stuff;

  private ExtraStuff() : base()
  {
  }

  public ExtraStuff(string stuff)
    : this()
  {
    this.stuff = stuff;
  }

  public string Stuff
  {
    get
    {
      return this.stuff;
    }
  }
}

We can now check to see if CallContext has an ExtraStuff instance and use that:

public sealed class Load : ILoaded
{
  private string stuff;
    
  public Load()
    : base()
  {
    object extra = CallContext.GetData(ExtraStuff.CallContextId);
        
    if(extra != null && extra is ExtraStuff)
    {
      this.stuff = (extra as ExtraStuff).Stuff;    
    }
  }

  public string Unload()
  {
    return this.stuff;
  }
}

Of course, the caller has to get that set for us - here’s a test method to illustrate this:

private const string Stuff = "window";

[TestMethod]
public void LoadWithCallContext()
{
  CallContext.SetData(ExtraStuff.CallContextId, 
    new ExtraStuff(LoadTests.Stuff));
  Assert.AreEqual(LoadTests.Stuff, Loader.Load().Unload());
}

This also works in multithreaded scenarios as this test method shows:

[TestMethod]
public void LoadWithCallContextAcrossThreads()
{
  using(EventWaitHandle handle = new ManualResetEvent(false))
  {
    string result = null;
        
    CallContext.SetData(ExtraStuff.CallContextId, new ExtraStuff(LoadTests.Stuff));
    Assert.IsTrue(ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
    {
      result = Loader.Load().Unload();
      handle.Set();
    })));            
        
    Assert.IsTrue(handle.WaitOne(5000, true));
    Assert.AreEqual(LoadTests.Stuff, result);
  }
}

I’m not saying this is an ideal solution. If you can get the information into a method via an argument, do it. But in the case where you’re stuck behind a pattern, this gives you a way out.

Published: 05.09.2007 09:17:00 PM CST