C# 3.0 - Partial Methods

Partial methods are a new feature available in C# 3.0 that don’t seem to get enough credit. I think there was a lot of confusion early on about what partial methods were and how they were used.

Partial methods are intended to solve a major problem that is not only caused by code-generation tools and also affects those same tools. For instance, you are writing a code-generation tool but want to provide a way for the developers that are using your generated classes to hook in to specific areas of the code. As such, you don’t want them editing your generated code since those customizations will be lost the next time the tool runs. On the flip side of this scenario is the developer who needs to write the code that hooks into those specific areas and doesn’t want that code being lost the next time the tool runs.

Delegates are one solution to this type of problem. In the generated code, you declare a delegate method that you then call at the appropriate areas. If no one has implemented a concrete version of the delegate, the call will not do anything. However, if someone has implemented am instance of that delegate then the call will run the code in that instance. This sounds like a pretty good solution, and, until partial methods it really was the only solution. The drawback is that the code for the delegate is always compiled in to the runtime of your application and add to the runtime overhead of application (granted, that overhead is minimal but it’s still there).

Partial classes helped with this problem by allowing the code-generation tools to isolate the generated code in a partial class. This allowed the developer to add their own methods to the class without fear that they would be overwritten the next time the tool ran. However, it only helped partially. In order to accomplish our scenario, you still needed to provide a delegate to allow the developer to hook into your process.

If we take this a step further and look at partial methods, you will start to see how they work and what the benefit is of using them. The following rules govern how partials methods can be declared and used:

  1. Must be declared inside a partial class.
  2. Must be declared as a void return type.
  3. Must be declared with the partial.
  4. Cannot be marked as extern.
  5. Can be marked static or unsafe.
  6. Can be generic.
  7. Can have ref but not out parameters.
  8. Cannot be referenced as a delegate until they are implemented
  9. Cannot have access modifiers such as public, private or internal.
  10. Cannot be declared as virtual.

Partial methods are implicitly marked as private. This means they cannot be called from outside the partial class.

Now that we have the rules out of the way, let’s take a look at an example. This is a completely contrived example, as you typically wouldn’t declare both portions of the partial class yourself.

// This is the class that would normally have been autogenerated.
public partial class CustomTypedCollection
{
    partial void BeforeAddingElement(CustomElement element);
 
    public void AddElement(CustomElement element)
    {
        BeforeAddingElement();
    }
}

public partial class CustomTypedCollection
{
    partial void BeforeAddingElement(CustomElement element)
    {
        Console.WriteLine("Element " + element + " is being added.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        CustomTypedCollection c = new CustomTypedCollection();
        c.AddElemeent(new CustomElement());
    }
}

In this example, the code-generation tool declares a partial method BeforeAddingElement. It is declared as just a function prototype with no implementation. Effectively, this tells the developer that if they want to hook in to the AddElement process, they can do so by declaring an implementation of the BeforeAddingElement method. If an implementation is declared, the implementation and the call inside AddElement will be compiled in to the runtime code. However, if no implementation is declared the call will be optimized by the compiler and it will be entirely eliminated in the runtime code.

This mechanism provides a lot of the same flexibility as delegates without the runtime overhead. It also allows the developer to work with a much smaller implementation of their portion of the partial class that contains only the few partial methods that have been implemented.