Skip to main content

Inferred Typing with Factory Methods as Extension Methods

By Peter Ritchie

When working with instance variables and most factory and conversion methods you need to repeat the type name for both the declaration and the call to the static methods.

For example:

static void Main ( )
{
  MyFirstType myObject = null;
  myObject = MyFirstType.Parse("10");

}
//...
classMyFirstType
{
  public MyFirstType(float value)
  {
    Value = value;
  }
  public float Value { get; set;}
  public staticMyFirstType Parse ( String text )
  {
    Single value = Single.Parse(text);
    return newMyFirstType(value);
  }
  public static Boolean TryParse ( String text, out MyFirstType result )
  {
    result = null;
    Single value;
    if (Single.TryParse(text, out value))
    {
      result = newMyFirstType(value);
    }
    return result != null;
  }

}
classMySecondType
{
  public MySecondType ( int value )
  {
    Value = value;
  }
  public int Value { get; set; }
  public staticMySecondType Parse ( String text )
  {
    Int32 value = Int32.Parse(text);
    return newMySecondType(value);
  }
  public static Boolean TryParse ( String text, outMySecondType result )
  {
    result = null;
    Int32 value;
    if (Int32.TryParse(text, out value))
    {
      result = newMySecondType(value);
    }
    return result != null;
  }

}

In Main, we’re using the familiar factory-like conversion method Parse to initialize our object based on some text.

The drawback is that if you change the MyFirstType to another type, you have to make sure you change all calls to Parse to use the new type: It can't be inferred from the variable type because its type is not used, which results in a compile error.  For example:

static void Main ( )
{
  MySecondType myObject = null;
  myObject = MyFirstType.Parse("10");
  // cannot implicitly convert error above
}

In Main, we’ve changed the type of our variable, but we’ve forgotten to hunt down all the occurrences of that same type when used with that variable.

Enter extension methods. Extension methods are static methods that act as instance methods.  This means that, while they're called like an instance method, the instance need not be non-null in order to call the extension method (assuming you don't use the this parameter within the extension method).  The result is that you can call extension methods on instance variables whose value is null and not automatically incur a NullReferenceException before calling the method.

For example:

classMyFirstType
{
  public MyFirstType(float value)
  {
    Value = value;
  }
  public float Value { get; set;}

}
static classMyFirstTypeExtensions
{
  public staticMyFirstType Parse ( thisMyFirstType me, String text )
  {
    Single value = Single.Parse(text);
    return new MyFirstType(value);
  }
  public static Boolean TryParse ( thisMyFirstType me, String text,
    out MyFirstType result )
  {
    result = null;
    Single value;
    if (Single.TryParse(text, out value))
    {
      result = newMyFirstType(value);
    }
    return result != null;
  }

}
classMySecondType
{
  public MySecondType ( int value )
  {
    Value = value;
  }
  public int Value { get; set; }

}
static classMySecondTypeExtensions
{
  public staticMySecondType Parse ( thisMySecondType me, String text )
  {
    Int32 value = Int32.Parse(text);
    return new MySecondType(value);
  }
  public static Boolean TryParse ( thisMySecondType me, String text,
    out MySecondType result )
  {
    result = null;
    Int32 value;
    if (Int32.TryParse(text, out value))
    {
      result = new MySecondType(value);
    }
    return result != null;
  }

}
//...
static void Main ( )

{
  MyFirstType myObject = null;
  myObject = myObject.Parse("10");

}

We’ve moved our static Parse methods from the non-static types to new static types and added a new parameter to each method to change that the method to an extension method.  Main now calls that extension method from the myObject variable instead of the type.

Now, when we perform the same change we’re no longer presented with a compile error because our new type also has that method:

static void Main ( )
{
  MySecondType myObject = null;
  myObject = myObject.Parse("10");
}

In our particular example we do nothing with the first extension method parameter (the parameter with the this modifier) because we want to be able to deal with null instances of specific types.  You could very well improve these extension methods to test if the first parameter is null and perform the action directly on the instance instead of acting like a factory or conversion method.