Freigeben über


Partial methods for Visual Basic

One of the new features that we are adding to the language is the concept of a private partial method. In order to discover the rationale behind this feature, let's consider a scenario where a designer generates some code and wishes to allow the user to customize certain behavior at certain points in the generated code.

Some standard ways to customize

Typically in object oriented systems, there are a few ways that generated code can provide hooks for user customization. The traditional way is that the generated code defines a base class (say Customer), and several virtual/abstract methods are defined and consumed in the base class (say, a ValidateAddress method is defined and consumed in the SubmitChanges method). The user then provides a class that derives from Customer and he overrides ValidateAddress so that custom validation can be performed.

    1:  Class Customer
    2:      Protected MustOverride Sub ValidateAddress()
    3:      Public Sub SubmitChanges()
    4:          ' do some work here
    5:          Me.ValidateAddress()
    6:          ' do some work here
    7:      End Sub
    8:  End Class
    9:   
   10:  Class MyCustomer : Inherits Customer
   11:      Protected Overrides Sub ValidateAddress()
   12:          ' validate the address here
   13:      End Sub
   14:  End Class

This approach has problems; in particular, requiring a user to inherit from a class to provide customization can lead to fragile code, coupling, etc (see your standard Design Patterns on why patterns that prefer aggregation over inheritance is preferred). One solution for this is to make use of delegates; instead of the user creating a new class and overriding methods, the user can provide functions that perform the custom validation and pass delegates to the SubmitChanges method.

    1:  Class Customer
    2:      Public Delegate Sub ValidateAddressDelegate()
    3:      Public Sub SubmitChanges(validateAddress As ValidateAddressDelegate)
    4:          ' do some work here
    5:          validateAddress()
    6:          ' do some work here
    7:      End Sub
    8:  End Class
    9:   
   10:  Sub validateAddress()
   11:      ' validate the address here
   12:  End Sub
   13:   
   14:  Sub UseCustomer(c As Customer)
   15:      c.SubmitChanges(AddressOf validateAddress)
   16:  End Sub

As you can tell by the example, there are some small difficulties with this approach; where should the method validateAddress live? Should I define a new class to hold these methods to be passed off as delegates? Or a new module? And you can imagine that if you want to validate every field in say a database using this approach, you have to create delegate types for each field, and this can get messy. VB9 makes things slightly easier because we are introducing relaxed delegates (more on this in a later post), but overall, this is an ok solution, maybe not the best.

Another possible solution might be to create an event and then raise the event, so that clients who are interested in the even just need to add a handler to the event. The problem here is that the event mechanism is very generic, and it's hard to limit the scope of the event.

In general, the problem with all of these solutions is the fact that validation is exposed by the generated code is visible outside the generated code. What if I only wanted the user's partial class to customize the validation? None of the above solutions really fit the bill, because the fact that there's customization going on leaks.

Partial methods to the rescue

This is the problem that partial methods solve. Consider the following example:

    1:  ' Generated by a designer
    2:  Partial Class Customer
    3:      ' Notice the Partial keyword, and the fact that the method body is empty
    4:      Partial Private Sub ValidateAddress(addr As Address)
    5:      End Sub
    6:   
    7:      Public Sub SubmitChanges()
    8:          ' do some work here
    9:          ValidateAddress(addr)
   10:          ' do some work here
   11:      End Sub
   12:  End Class
   13:   
   14:  ' This is the user part of the partial class
   15:  Class Customer
   16:      ' Note that the signature of ValidateAddress matches the partial signature
   17:      ' but with the partial keyword removed
   18:      Private Sub ValidateAddress(addr As Address)
   19:          ' Do some validation
   20:      End Sub
   21:  End Class

Line 4 above introduces the concept of the partial method declaration. The use of the Partial keyword indicates to the compiler that this is the declaration of the method. The method body is empty. Notice that in same class, SubmitChanges calls the partial method.

Then in the user class (your class that customizes the partial class defined by the designer), you can provide an implementation for the partial method (line 18). The Visual Basic IDE has some cool support to help you generate the exact signature stub, so that you don't have to.

When the compiler sees the implementation and it matches a private partial method declaration, the method is now "live" and the call to ValidateAddress in SubmitChanges (line 9) calls the ValidateAddress method you specified in line 18.

So now you can extend the code generated by the designer, without leaking the customization points outside the class.

As a bonus, if your customer class does not provide an implementation for the ValidateAddress method (ie, lines 18 to 20 are removed), then the compiler does not have an implementing method for the ValidateAddress partial method, and as a result, the call in line 9 is completely removed by the compiler. So there is no overhead in partial methods at all.

In future versions of Visual Basic, we may revisit and extend the design of partial methods, but I think what we have in VB9 is valuable for these sets of scenarios.

In particular, you'll find that the LINQ to SQL designer makes use of partial methods in the code that it generates.

How can you use this?

This is all cool and all, but how can you take advantage of partial methods?

For the most part, your interaction with partial methods will be through code generated by designers such as the LINQ to SQL designer, using the pattern I described above.

In addition, you may choose to split a class across separate files for sake of collecting and factoring ideas into their own files; in this case, you may want to separate "stub" or "boilerplate" logic with customization logic. Here, it may make sense to define partial methods and use them in the stub partial class, and customize it in the partial class defined in another file.

As always feel free to let me know if you have any questions.

Technorati tags: VisualBasic, VB9, PartialMethods, Orcas

Comments