P

By now, most of you are likely familiar with the concept of partial classes, introduced in .NET 2.0 to both C# and Visual Basic.  Simply put, classes can be marked with the partial keyword indicating that their implementation is split over multiple physical source code files. 

This technique is quite apparent when you look at Windows Forms code files, where the designer.cs (or .vb) file contains a partial class representing the form.  The designer class file contains a lot of glue code to create and initialize form controls and properties, so mucking around in it to add your own code and customization has always been an invitation for disaster: one errant Ctrl-X and you could be in for some rework.

With partial classes though, you can create another code file and just re-declare the class, again with the partial keyword, and via compiler magic the two separate code files will be combined into a single binary image – just as if you’d coded it in one place.  It’s essentially syntactic sugar that helps promote separation of concerns and maintainability within your applications.

So that’s a partial class, but I though we were talking about partial methods?!

Indeed!  Partial methods are less well known, but offer an interesting construct for extensibility at a lower level.  You can think of a partial method as a place holder for an implementation that may (or may not) be provided. 

One technology that demonstrates the value and power of partial methods is the ADO.NET Entity Framework (as does its cousin, LINQ to SQL).  The Entity Framework is an object-relational mapping (ORM) framework that exposes an underlying data model as a set of .NET classes, essentially ‘objectifying’ and abstracting the data model, so that you can program to objects and let the framework worry about persisting modification to the ultimate data store, often a relational database.

With the Entity Framework you can generate a data model that maps objects directly to database tables (although the technology as a whole is far more powerful than that).  So, for instance, when I use the Entity Framework against the venerable Northwind database, I’ll get a series of classes representing entities (here, tables) in the model, for example.

[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(
    NamespaceName = "NorthwindModel", Name = "Categories")]
[global::System.Runtime.Serialization.DataContractAttribute(
    IsReference = true)]
[global::System.Serializable()]
public partial class Categories : 
    global::System.Data.Objects.DataClasses.EntityObject
{
...
}
 
[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(
    NamespaceName = "NorthwindModel", Name = "CustomerDemographics")]
[global::System.Runtime.Serialization.DataContractAttribute(
    IsReference = true)]
[global::System.Serializable()]
public partial class CustomerDemographics : 
    global::System.Data.Objects.DataClasses.EntityObject
{
...
}

Note, the classes here, Categories and CustomerDemographics, are partial classes as well.  Like a Windows Forms designer file, this model file is not really meant to be edited, since it will be refreshed whenever the model is modified through the visual design surface.  Extensions should be made in a separate file where these classes are ‘repeated’ and additional code added to customize the behavior according to the needs of the application.

As you might suspect, if each table is represented as a class, each column in the table is represented as a property.  So just as the Northwind table has a CategoryID, CategoryName, Description, and Picture column, so will the Entity Framework class have properties with the same names and types.  The class actually exposes additional properties as well, namely collections that reflect relationships with other tables.  So for instance, the Categories class has a property called Products, which contains all of the products in the specific category.  This particular property exist because of the primary/foreign key relationship between the two tables, thereby making manifest the power of ORM technologies!

Let’s take a look at the implementation of one of those properties, CategoryName:

   1:  [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute
   2:        (IsNullable = false)]
   3:  [global::System.Runtime.Serialization.DataMemberAttribute()]
   4:  public string CategoryName
   5:  {
   6:      get
   7:      {
   8:      return this._CategoryName;
   9:      }
  10:      set
  11:      {
  12:      this.OnCategoryNameChanging(value);
  13:      this.ReportPropertyChanging("CategoryName");
  14:      this._CategoryName = global::System.Data.Objects.DataClasses.
  15:                        StructuralObject.SetValidValue(value, false);
  16:      this.ReportPropertyChanged("CategoryName");
  17:      this.OnCategoryNameChanged();
  18:      }
  19:  }
  20:  private string _CategoryName;
  21:  partial void OnCategoryNameChanging(string value);
  22:  partial void OnCategoryNameChanged();

Note lines 21 and 22 where two of the methods are defined with the partial keyword.  These methods are invoked by the property setter at lines 12 and 17, and can be thought of as lightweight events.

Currently, there is no implementation for those methods, so when this class is compiled the calls are essentially removed and there will be no trace of the method in the resulting IL (Intermediate Language).  Since the implementation is optional, it’s a cleaner alternative than conditional compilation (#IF DEFINED) or dummy virtual method implementations.

Let’s say though that I want to extend the class definition here and ensure that the CategoryName always starts with a letter and is at least 5 characters long.  To enforce that constraint, I can provide an implementation of OnCategoryNameChanging to enforce the rule and throw an exception if the value to be assigned does not meet the requirements.  So, I’ll create a partial class in another file and implement the partial method there:

using System;
using System.Text.RegularExpressions;

namespace PartialMethodTest
{
    public partial class Categories
    {
        partial void OnCategoryNameChanging(string value)
        {
            if (!Regex.IsMatch(value, "^[A-Z].{4}") )
                throw new ArgumentException("Category Name is not valid");
        }
    }
}

Now we need to write a bit of code to test this out.  I’m just using a simple console application to instantiate the model and grab the first category in the list.  Then, I hardcode an invalid name to trigger the exception:

using System;
using System.Linq;

namespace PartialMethodTest
{
    class Program
    {
        static void Main(string[] args)
        {
            NorthwindEntities ctx = new NorthwindEntities();
            Categories firstCategory;

            firstCategory = ctx.Categories.FirstOrDefault(c => true);
            Console.WriteLine("The original category is: " +
               firstCategory.CategoryName);

            try
            {
                firstCategory.CategoryName = "1NET";
            }
            catch (ArgumentException exArg)
            {
                Console.WriteLine(exArg.Message);
            }

            Console.WriteLine("The modified category is: " + 
               firstCategory.CategoryName);
            Console.ReadLine();
        }
    }
}

The result, as you might expect, is something like:

Console

So, with partial methods, I can pretty handily build a validation layer that is separated from my actual model.  If I need to swap in a different set of validation logic to customize an application for one customer versus another, it’s a piece of cake.

The last thing I want to do here is drive home the point that a partial method invocation is not compiled into IL unless there is an implementation provided for it.  To show that, let’s point ILDASM at the generated assembly, and look at the implementation for the setter of the CategoryName property. You can see a call to OnCategoryNameChanging but no call to OnCategoryNameChanged, which if you take a look at the C# code earlier in this post, is the last method that would be invoked in the setter.  It’s not in the IL, because it’s a partial method, and we did not provide an implementation for it – no execution overhead and no code bloat for functionality you opt out of.  Pretty cool, huh!

image