Using partial methods to extend generated code


To wrap up a bit on the partial class support for generated code scenario, let's look at a case we missed. Although our workflow is great for "structural" code (fields and simple properties and such), what about methods? For example, let's say that we want to add validation logic to a property setter.


The problem we'll run into is that the property setter has already been defined in the generated file, and there's no way to extend this directly. Well, we can always add code to the generated file, but we're back to square one with the problem of having our changes overwritten if the code is re-generated.


The solution C# offers is the use of partial methods. Partial methods can be declared more than once, but must have a body only in at most a single file. The interesting thing is that it's OK for a partial method to have no body it all, in which case the compiler can simply ignore the method everywhere - no calls will be made, and the method itself won't exist in the assembly.


For example, we can have a FirstNameChanged method that takes in the new value of the property declared in the code-generate file, and called by the FirstName property setter. If we choose to supply an implementation for the method in another file, we'll get called - otherwise, nothing happens.


Note that there are restrictions as to what the signature of a method may look like - in general, you can't depend on values being returned directly or through out parameters, but modifying things through ref parameters (or mutable parameters) is OK.


For example, let's say I have a file with this code.



namespace CsNamespace
{
 
using System;
 
using System.Text;

 
public partial class Cs
 
{
   
partial void SayHello();
   
partial void SayHelloHere(StringBuilder builder);

   
public static void Main(string[] args)
    {
      Cs cs =
new Cs();
     
Console.WriteLine("Invoking SayHello...");
      cs.SayHello();

     
StringBuilder sb = new StringBuilder();
     
Console.WriteLine("Invoking SayHelloHere...");
      cs.SayHelloHere(sb);
      if (sb.Length == 0)
      {
       
Console.WriteLine("Nothing happened.");
      }
     
else
     
{
        
Console.WriteLine("Something happened!");
       
Console.WriteLine(sb.ToString());
      }
    }
  }
}


Now, if I compile it on its own, this is the output I get.



Invoking SayHello...
Invoking SayHelloHere...
Nothing happened.


However I can go ahead and add an additional file to the project.



namespace CsNamespace
{
 
using System;
 
using System.Text;

  public partial class Cs
 
{
   
partial void SayHello()
    {
     
Console.WriteLine("Hello");
    }

   
partial void SayHelloHere(StringBuilder builder)
    {
      builder.AppendLine(
"Hello");
   
}
  }
}


Now, if I compile both files and run, this is the output I get.



Invoking SayHello...
Hello
Invoking SayHelloHere...
Something happened!
Hello


As you can see, all the partial calls were now made into the implementation I defined in the extra file.


Enjoy!

Comments (0)

Skip to main content