6

Over at this question in the comments I mentioned that I heard that partial classes are best avoided if possible.

What if any is the reason for this sentiment? Or If this is an invalid sentiment, how are the perceived detriments overcome?

6 Answers 6

9

Aside the designer-generated code, partial classes can still be useful without compromising the code quality.

Examples of correct usage

A few usages I found particularly useful are the following:

  1. SQL queries

    I do a lot of stuff where direct, hand-written SQL queries are used instead of ORMs. The most basic architecture is to embed queries directly in code:

    public class Demo
    {
        [...]
    
        public int GetPriceOfProduct(long productId)
        {
            var query = Query(
                "select top 1 [Price] from [Shop].[Products] where [ProductId] = @ProductId",
                this.ConnectionString);
    
            // Use the query to retrieve the price.
        }
    
        [...]
    }
    

    This works well, until you have too many methods and until too many queries start to have more then ten lines. Reviewing only the queries by the DBA is complicated too, since the DBA has to search through all the code. In this case, the queries may be put in a separate class:

    public class Demo
    {
        [...]
    
        public int GetPriceOfProduct(long productId)
        {
            var query = Query(Queries.PriceOfProduct, this.ConnectionString);
    
            // Use the query to retrieve the price.
        }
    
        [...]
    
        private static class Queries
        {
            public const string PriceOfProduct =
                @"select top 1 [Price] from [Shop].[Products] where [ProductId] = @ProductId";
        }
    }
    

    Once done, the Queries subclass is a good candidate to be put in a separate file.

    One way is to make it internal, rename to DemoQueries and create a DemoQueries.cs file. The issue with that is that the code will be longer (specifying ProductOfUserRelationQueries instead of just Queries is actually a bit longer), and that the class will pollute the assembly (including the Intellisense), while you need to access it only in Demo class.

    Another way is to make Demo partial, and to have the queries in a separate file, keeping the Queries class inside Demo.

  2. Names in cache

    When you cache some elements, it is important to be consistent with names (keys) used in cache. For that and similar to the first example, you can end up with a private class containing a bunch of methods like this:

    public class Demo
    {
        [...]
    
        private static class CacheNames
        {
            public static string PriceOfProduct(long productId)
            {
                return string.Format(@"Price(ProductId<{0}>)", productId);
            }
        }
    
        [...]
    }
    
  3. Trace

    The same applies to trace. Sometimes, a code which relies a lot on trace is nearly unreadable since the trace IDs and messages waste too much space. Putting a this.Trace(TraceId.UserRemoved) makes the code much shorter, but you have to define all the TraceIds and their associated event IDs and messages somewhere.

  4. Overloads

    This case is rather weird and has to be avoided in most cases, but still, there are valid examples where partial classes help.

    I had a project where methods had a huge amount of overloads and other methods which were not doing anything useful rather than calling a different method while changing some parameters. They were required because they were used by the consumers of the library, but it was really annoying to see all them in the source code and being distracted from the real code (note that even an overload can be quite long in terms of LOC: at least three lines for the XMLDoc, but sometimes it was more about twenty lines, then the method itself, with its code contracts and then a tiny little call to the same method with different parameters).

    Putting in a separate file all the overloads and methods which weren't doing too much helped concentrating on the core code.

Partial classes are the same as with regions

#regions are bad, and one of the reasons why they are so bad is that they give you an impression that your code is small, when in fact it has thousands of lines and is completely unreadable. The major issue is when somebody uses them inside of a method, to end up with hundreds of lines of code in this method which now does twenty things instead of one.

Partial classes have the same issue: if you rely too much on them, you can have an impression that your class is rather small, while it should have been refactored for months.

They may make the code difficult to navigate

Another flaw with partial classes is that it's not so easy to know where the stuff is. Take Initialize() in Windows Forms. For a person who discovers Windows Forms and partial classes, it's not so easy to understand where this method is: is it a method of the parent Form class, and is a part of .NET Framework? Or maybe it's somewhere in the code for MyForm? Oh, no, it's in a designer-generated code.

Yes, Visual Studio has the F12 - Go to Definition feature, but if you can organize your code into files so the person can tell where a method is, do it.

7

One of the pitfalls is to think partial classes are somehow related to project modularity. This situation is perfect example.

Partial classes are only good if you have parts on single class created by completly different means. Common use is to have one piece of class generated by some tool (UI designer, model class generator, service generator) and another part added by developer. The tool-generated part can be regenerated at any time. Not so with the part added by developer.

1
  • +1 to explanation, we actually do use partial classes for attribute decoration purposes of generated classes from Database in a MVC project .
    – Yusubov
    Commented Jul 25, 2012 at 10:21
1

One danger not yet mentioned with partial classes is that there is no way--short of adding an extra level of inheritance--of indicating that all object construction must be done through a particular constructor. Given:

class Fnorble
{
  protected readonly int[] biz,boz;
  public Fnorble(int size)
  {
    biz = new int[size];
    boz = new int[size];
  }
}

One might hope that one could assume as an invariant that biz and boz will always be identically-sized arrays, but unless one searches through the entire project one can't really be sure. If another file to contained a definition:

partial class Fnorble
{
  public Fnorble(int bizSize, bozSize)
  {
    biz = new int[bizSize];
    boz = new int[bozSize];
  }
}

then the previously-mentioned invariant would not hold.

0

There is one situation I was confronted with quite a couple of times before.

Although partial classes allow the concept of partial methods they don't allow partial constructors. So if you are using machine generated code it's most probably that there will be a constructor defined already which means you are unable to add custom construction code.

One common work around for this is of course to call other 'constructing methods' directly from the one defined constructor like having

// example constructor of the partial class
public PartialClass {

    partial void Initialize1();
    partial void Initialize2();
    partial void Initialize3();
    // ...
}

but that requires the explicit use of such a pattern which is of course not the case when working with foreign or machine generated code.

1
  • can't you just overload the constructor?
    – Bob
    Commented Aug 19, 2012 at 20:50
0

Partial classes are a must in .Net in my experience. For example a window in WPF starts out as a partial class since a lot of the implementation is boiler plate that is automatically generated for you.

Another case is when using Entity Framework or other code generators. It will create objects that represent things in the database. You could add your modifications to do for example adding calculated properties to the class in the generated code file but that wouldn't be ideal because whenever you modify your model the code generator would wipe out your custom features. So you should add a partial class outside of the generated code that extends it to add the features you want.

No absolute rule can be made (even this one :-)) about best practices because the problems being solved can be so different. Example: don't use GOTOs except that at the lower level the compiler/assembler code has GOTOs all over the place to implement for loops and ifs.

I'd replace the no partials rule with: if things are so complicated you think you need to move part of the code to another file give a good thought over whether you need to break things down into smaller classes. If a smaller class solution doesn't make sense go nuts with partials.

0

IMHO there is only one case where using partial classes makes sense. But in that case it does make a lot of sense.

And that case is when you are using a tool to automatically generate code, e.g. based on a database schema, XSD schema files, or something else, and you need to add functions to the auto-generated code classes.

In that case, partial classes is a great tool to allow you to write custom code that doesn't get overridden the next time the code generation tool is executed. I had one example where we added some interfaces to classes generated from an XSD schema in order to avoid duplication in the code processing the corresponding XML data.

But of you are writing all the code by hand, splitting the code into partial classes is a code smell, an indication that your class is growing too big/having too many responsibilities.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.