Click here to Skip to main content
15,867,835 members
Articles / Programming Languages / C#

Implanting Common Code in Unrelated Classes

Rate me:
Please Sign up or sign in to vote.
4.45/5 (3 votes)
6 Dec 2009CPOL4 min read 29.1K   43   4   13
Using partial classes to share common code when a base class and inheritance aren't appropriate

Introduction

In this article, I'll present a technique I came up with to allow code re-use among classes for which no logical relationship exists.

Background

For a project I'm working on, I have three classes that all need to be able to raise an event that uses a custom EventArgs. All three classes contain exactly the same code to do this, and when I wanted to alter the code I had to alter it in all three classes. Obviously, I would prefer to have one master copy of the code that the classes could share. I can hear you saying, "Make a base class and derive from it!", and yes, I could do that. In fact I did that as a temporary measure, but I wasn't happy with the result.

The main problem with having these classes derive from a common base class is that it defines a relationship between them that really doesn't exist. A further problem is that C# only allows single inheritance, so if any of these classes (or some future class) needs to derive from some other class, I would have to choose which to derive from. So, given that inheritance isn't really appropriate in this case, I chose to seek another way to share this code.

I will also note that using an interface will not help in this situation, because it doesn't offer implementation sharing. Inheritance offers a relationship with implementation sharing, an interface offers a relationship without implementation sharing, but I want implementation sharing without the relationship.

I'm not sure whether or not mixins would do what I want, but it doesn't matter because C# doesn't support them anyway.

What I Came Up With

I'm a big fan of partial classes[^] (most of my large classes are split among several files to segment the code into logical groups of members) so it is natural that I maintain one copy of the common code, duplicate it for each class that needs it, and have it included as one file of a partial class.

The only real obstacle to overcome is that each individual copy of the common code needs to contain the name of the class (and its namespace) in which it is to be implanted.

So, basically: copy the file and replace some text in the copy.

Sound familiar? That's what the C-preprocessor[^] does best. As a quick and dirty implementation of this technique of using partial classes to implant common code in unrelated classes, I'm using the C-preprocessor. If you want to write a utility to do it, go right ahead, I just don't feel like it right now. The particular implementation of the C-preprocessor I'm using is the one I got with WinGW; I don't remember where I got it or where it's available now so I can't include a link. I hope someone posts a message with a link. Or you may use your own favorite C-preprocessor.

Details of this Technique

  1. The classes that are to receive the implanted code must be marked partial.
  2. Decide on place holders for the namespace and class names and write the common code file:
    C#
    namespace __NAME_SPACE__
    {
        partial class __CLASS_NAME__   // You may add interfaces if you like
        {
            // The common code goes here
        }
    }
  3. If you are using Visual Studio, you can include this file in your project, but you may want to set the Build Action to None so it doesn't get built into the assembly.
  4. I chose to write a small BAT file (Implant.bat) to help with calling the C-preprocessor:
    "C:\mingw\bin\cpp" -P -C -include "c:\batfiles\ImplantWarning.h" 
    	-D__NAME_SPACE__=%3 -D__CLASS_NAME__=%4 -w "%1" "%2"
  5. The BAT file can then be called with: IMPLANT sourcefile destfile namespace classname. The C-preprocessor will:
    1. Read the file specified in the first parameter.
    2. Insert the text of ImplantWarning.h (see below).
    3. Replace any instance of __NAME_SPACE__ with the value of the third parameter.
    4. Replace any instance of __CLASS_NAME__ with the value of the fourth parameter.
    5. Write the resultant text to the file specified in the second parameter.
  6. The BAT file includes ImplantWarning.h to prepend the following warning in the generated file:
    C++
    /* This is a generated file, any modifications may be lost. */

    Both these files are included in the zip file, and you may change either as you see fit.

  7. If you are using Visual Studio, you can put these statements in the project's pre-build event.

    In my project's case, I added:

    call Implant.bat ..\..\Types\MessageEventImplant.cs ..\..\dal\
    dalVersionControl.MessageEventImplant.cs PIEBALD.VersionControl dalVersionControl
    call Implant.bat ..\..\Types\MessageEventImplant.cs ..\..\opr\
    oprVersionControl.MessageEventImplant.cs PIEBALD.VersionControl oprVersionControl
    call Implant.bat ..\..\Types\MessageEventImplant.cs ..\..\api\
    apiVersionControl.MessageEventImplant.cs PIEBALD.VersionControl apiVersionControl

    Note that call is required when calling BAT files repeatedly.

  8. Once you have generated the implant files, you can refer to their contents in your code.
  9. If you are using Visual Studio, you can use Add existing item to add the generated files to your project.
  10. The debugger will be able to step through the implant file, but any changes should be made to the common file.

Limitations

Because this technique uses the C-preprocessor, the common code can't contain preprocessor directives. If this is a problem, you can write your own utility.

It's a source-code-level technique, you can't implant code that's already compiled into an assembly.

Conclusion

In many cases, inheritance has been used simply to provide code-re-use, even when it is inappropriate. This technique provides code re-use without adding unnecessary inheritance. It can eliminate some of the mis-use of inheritance (and multiple inheritance in languages that allow it) by providing another form of aggregation.

History

  • 2009-12-05 First submitted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
BSCS 1992 Wentworth Institute of Technology

Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.

OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB, acknowledged pedant and contrarian

---------------

"I would be looking for better tekkies, too. Yours are broken." -- Paul Pedant

"Using fewer technologies is better than using more." -- Rico Mariani

"Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’" -- Steve McConnell

"Every time you write a comment, you should grimace and feel the failure of your ability of expression." -- Unknown

"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]

"Typing is no substitute for thinking." -- R.W. Hamming

"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup

ZagNut’s Law: Arrogance is inversely proportional to ability.

"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon

"linq'ish" sounds like "inept" in German -- Andreas Gieriet

"Things would be different if I ran the zoo." -- Dr. Seuss

"Wrong is evil, and it must be defeated." –- Jeff Ello

"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw

“It’s always easier to do it the hard way.” -- Blackhart

“If Unix wasn’t so bad that you can’t give it away, Bill Gates would never have succeeded in selling Windows.” -- Blackhart

"Use vertical and horizontal whitespace generously. Generally, all binary operators except '.' and '->' should be separated from their operands by blanks."

"Omit needless local variables." -- Strunk... had he taught programming

Comments and Discussions

 
GeneralFurther refinements Pin
PIEBALDconsult14-Mar-13 18:47
mvePIEBALDconsult14-Mar-13 18:47 
QuestionP.S. Pin
PIEBALDconsult17-Oct-11 15:50
mvePIEBALDconsult17-Oct-11 15:50 
QuestionTemplates? Pin
Owen Lawrence7-Dec-09 7:30
Owen Lawrence7-Dec-09 7:30 
AnswerRe: Templates? Pin
PIEBALDconsult7-Dec-09 11:09
mvePIEBALDconsult7-Dec-09 11:09 
GeneralRe: Templates? Pin
r_hyde7-Dec-09 15:21
r_hyde7-Dec-09 15:21 
GeneralRe: Templates? [modified] Pin
PIEBALDconsult7-Dec-09 15:38
mvePIEBALDconsult7-Dec-09 15:38 
GeneralRe: Templates? Pin
Owen Lawrence8-Dec-09 4:22
Owen Lawrence8-Dec-09 4:22 
GeneralRe: Templates? Pin
PIEBALDconsult8-Dec-09 5:52
mvePIEBALDconsult8-Dec-09 5:52 
GeneralAdditional thoughts Pin
PIEBALDconsult7-Dec-09 7:04
mvePIEBALDconsult7-Dec-09 7:04 
GeneralGenerated code at compile-time Pin
User 30424986-Dec-09 9:31
User 30424986-Dec-09 9:31 
GeneralRe: Generated code at compile-time Pin
PIEBALDconsult6-Dec-09 9:53
mvePIEBALDconsult6-Dec-09 9:53 
GeneralRe: Generated code at compile-time Pin
PIEBALDconsult6-Dec-09 10:18
mvePIEBALDconsult6-Dec-09 10:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.