What's the difference between a partial method and a partial class?

What's the difference between a partial method and a partial class?

Rate This
  • Comments 33

Like "fixed" and "into", "partial" is also used in two confusingly similar-yet-different ways in C#.

The purpose of a partial class is to allow you to textually break up a class declaration into multiple parts, usually parts found in separate files. The motivation for this feature was machine-generated code that is to be extended by the user by adding to it directly. When you draw a form in the forms designer, the designer generates a class for you representing that form. You can then further customize that class by adding more code to it. If you can edit the machine-generated code then any number of problems arise. What if it is re-generated? What if the machine is using the code itself as a persisted state for design-time information about the class, and your edit messes up the machine's parser? It's much better to simply put the machine-generated half in its own file, generate a comment that says "can't touch this", and put the user code in its own location.

There are other uses of partial classes that do not involve machine-generated code but they are relatively rare. Some cases where I see partial classes used are:

  • If a class is really large and implements a bunch of interfaces, sometimes one interface implementation per file makes sense. More often though this is a bad code smell which indicates a class that is trying to do too much; it might be better to split this thing up into multiple classes.
  • It's somtimes nice to put nested classes in their own files; making the containing class partial is the only good way to do that.
  • And so on.

Partial methods are a subtly different story. Like partial classes, partial methods are about combining multiple declarations of the same method to make machine-generated code scenarios better. But though the high-level purpose of the feature is the same, the details are rather different.

The way a partial method works is there are two declarations, the "latent" declaration and the "actual" declaration. The latent declaration does not have a body, like it was an abstract method. The actual declaration does. The latent declaration goes in the machine-generated side of a partial class, the actual declaration goes in the human-authored side. If there is an actual declaration, then the latent declaration is completely ignored. But if there is no actual declaration then all calls to the method are removed, just as it the method were compiled with the conditional attribute! And in fact, the latent declaration is also logically removed when we spit out the metadata for the generated class; it's as if it never was.

The reason for this behaviour is because we wanted to enable this scenario:

 // Machine generated code:
partial class MyFoo
{
  void ButtonClickEventHandler(/*whatever*/)
  {
      // call user code to see if they want to do anything at this time
      OnBeforeButtonClick(whatever);
      blah blah blah
      // call user code again
      OnAfterButtonClick(whatever);
  }
  partial void OnBeforeButtonClick(/*whatever*/); 
  partial void OnAfterButtonClick(/*whatever*/);
...

The user is going to be customizing the partial class; by putting in partial methods, the machine-generated side can create simple customization points all over the show that the user can then implement. But consider the down sides of this in a world without partial methods. The user is forced to implement empty methods. If they do not, they get errors. There are potentially hundreds of these empty methods to implement, which is vexing. And each of those methods generates a non-trivial amount of metadata, making the final binary larger than it needs to be. Disk space is cheap but network latency has replaced disk space as the factor that discourages large assemblies. If we can eliminate that metadata burden, that would be great.

Partial methods fit the bill. The user can provide actual implementations for as many methods as they choose, and now this becomes a "pay to play" model; you get as much implementation expense as the number of actual methods you implement.

The name of the feature during the design process was "latent and actual methods"; we strongly considered adding new keywords "latent" and "actual". But since the feature only makes sense in partial class scenarios, we decided to re-use the existing contextual keyword "partial" and renamed the feature. Hopefully the consonance between the two uses of "partial" helps more than the subtle differences hurt.

SUPER EXTRA BONUS: Yet more partiality

We considered adding a third kind of "partiality" to C# 4; this feature made it through the design phase but got cut before implementation. (If there is high demand, we'll consider adding it to hypothetical future versions of C#.) 

Sometimes you're in a machine-generated code scenario like this:

// Machine generated
partial class C
{
  int blah;
...

and then in the user-generated side of things, you want to do something like:

// User generated
partial class C : ISerializable
{

And oh heck, I need to put the NotSerialized attribute on blah, but I cannot edit the text of blah because when it gets re-generated, that will be lost.

The idea of this new kind of partiality is to re-state the declaration of a member -- a field, method, nested type, whatever -- with metadata attributes. It's like a latent/actual method but in reverse; the "actual" thing is in the machine-generated side, the "latent" thing in the user-generated side is just there to add metadata:

[NotSerialized] partial int blah; // not actually a declaration of a field

I like this feature but during the design process I pushed back hard on using the "partial" keyword to have a third meaning subtly different from the other two. Adding this confusion once seemed justifiable, but twice? That's pushing it. We therefore settled on adding another contextual keyword:

[NotSerialized] existing int blah; // not actually a declaration of a field

Decorating a declaration with "existing" would mean "this is not a real declaration, this is a mention of an existing declaration; please verify that such a declaration exists somewhere else in this partial class, and add this metadata to that member".

Like I said, this handy feature got cut because of resource constraints. If you have really awesome scenarios that this would make easier, I'd love to hear about them; obviously I cannot make any promises about possible future features of unannounced, entirely hypothetical products. But real user scenarios are a highly motivating factor in getting budget for features.

 

  • Blake: I like and use this sort of code organization, but the prevailing design orthodoxy frowns on the kind of "classes" which it produces.

    > And in fact, the latent declaration is also logically removed when we spit out the metadata for the generated class

    This is logical, but I got tripped up by this tidbit while working with my macro extension for C# and had to drop the "partial method for generated code + normal method which calls the partial" idiom. Anyway, there are rather severe limitations on the signatures of partial methods — again perfectly logical, but not very convenient for general use.

    Ram & Pop Catalin: agree. PS: Pop Catalin, do you play Go by any chance? :)

  • @Antun Tykhyy: That would be Catalin Taranu and Pop Cristian :)

  • @ Anton TykhyyPS:  "Pop Catalin, do you play Go by any chance? :)"

    Well not really a favorite game of mine, but I love and do play chess. Sorry to everyone for the OT but why you ask?

  • Christian Pop and Catalin Taranu are the best Romanian Go players, both 7 dan. Goran — bull's-eye!

    http://www.europeangodatabase.eu/EGD/Player_Card.php?&key=10333389

    http://www.europeangodatabase.eu/EGD/Player_Card.php?&key=10586785

  • Other than the disk space savings, what benefit is there to partial methods over virtual methods? Also, did anyone discuss re-tasking the virtual/override keywords for fields in the "partial field" scenario rather than  keyword? It seems to be much more conceptually related than what you've done here, and could be used more readily in an inherited design.

    The purpose of an inheritance relationship is to express an "is a kind of" relationship between two concepts in the domain being modelled. That you can use an inheritance relationship as a mechanism for providing extensibility points from a machine-generated class to a user-customized class does not imply that you should. Inheritance is a powerful and complex mechanism and in single-inheritance languages you only get to do it once, so you need to "spend" your use of inheritance carefully.

    When we designed the VSTO architecture we had a huge debate over what parts should use inheritance, what parts should use aggregation, what parts should use events, and so on. Each have their own benefits and drawbacks.

    -- Eric

  • I've just spent the past several days working on a code-gen system and figured I'd have to use partial classes and methods to implement some "abstract" functions, so thanks for the timely post!

    I'm not sure what some other people here are on about with respect to the system being useless or unwieldy - I think once you actually start working on a code gen, it's obvious how necessary and (relatively speaking) easy it is.  I just wish that there was a simpler way to get all of the code gen integrated with the IDE, without the massive overkill and inflexibility of the DSL.  All this mucking about with COM registrations and registry settings and XML schema catalogs... sigh.

  • I like Doug's idea for a class level NonSerialized attribute, but unfortunately that would only fix the NonSerialized attribute. You really need something like PostSharp, so that you can add any attribute to any machine-generated member at compile-time.

    (Sorry if this shows up multiple times - the MSDN blogs seem to be blocking me!)

  • Why is it necessary to use two keywords partial and existing instead of using only existing?

  • One of the big wins for partial classes and generated code is the ability to make a generated class implement a user-defined interface.  I've found this very useful with Linq2Sql and unit testing.

  • Ironically I was lamenting this feature being missing today, as it is going to add a lot of extra work for us, we would like to decorate the properties on fields returned from a WCF service with Data Validation attributes for our Silverlight app. This not being possible without completely hand coding the data objects in the service references, it's back to the drawing board.

  • I'm not in favour of having a new keyword for this purpose. The idea is an extension of partial classes, it makes sense to use the same keyword and it's not hard to understand.

  • I'm not in favour of having a new keyword for this purpose. The idea is an extension of partial classes, it makes sense to use the same keyword and it's not hard to understand.

  • Hi, I would actually agree with the first commenter, Pavel Minaev [MSFT]. Metadata is getting more and more important in entity framework and users of it. It started with "dynamic data", and is now also used for validation for example in silverlight 3 (http://msdn.microsoft.com/en-us/magazine/dd695920.aspx#id0420160)

    This is now solved by adding a "metadata" class (see http://mattberseth.com/blog/2008/08/dynamic_data_and_custom_metada.html or the above msdn link for example). But of course this is a bad (but working) workaround, since you can make all kind of typos in your meta data class, and you won't find out until runtime :-(

    So hope fully this is a strong enough case?

    (b.t.w. I would also kind of expected that it would have the partial decoration, in line with the rest of the usage of partial...)

    Thanks for reading

  • Very cool idea with the member existing keyword. I would think that would be very helpful for things like documentation for example. Lots of code generation utilities do not put in the nessecary XML comments which results in compiler warnings for missing documentation members, currently the only way to fix that is to modify the machine generated code.

  • Anyone who uses code generation feels the pain that Existing would alleviate. There isn't much you can do today as a consumer of generated code and it has become more prevalent with the advent of EF and RIA Services.

    The buddy classes are an awful hack. They're not discoverable.

    Altering the template is a heavy weight option that is just not realistic for most developers.

    So "Existing" would be a most welcome feature.

    But why limit it to supplementing generated members with attributes? Why not let me change accessibility, virual/sealed, anything so long as I end up with the same signature as the doppelganger in the generated code.

    In fact, why not let me rewrite the member entirely?

    It's really a compiler issue, right? This is not that far from what you have to go through with partial methods.

    You would have to do something about conflicting instructions anyway. If the generated method says [Serializable] and I want it to be [Nonserializable], I win. The member marked "Existing" always wins.

    What about multiple partial files? You had to address that one for your attribute example. When you know how to do that, you might as well do the same for a total rewrite.

    Now all you have to do is work out the syntax :-)

    Ultimately, AOP is going to deal with most of this. The one place AOP won't ... is changing public/protected/private/... and virtual/sealed. Existing could still have value here.

Page 2 of 3 (33 items) 123