5,702,921 members and growing! (17,912 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate

Introducing C# 2.0 Partial Types

By Edward Moemeka

A general discussion on C# 2.0 Partial Types
C#, Windows, .NET, Visual Studio, Dev

Posted: 3 Oct 2005
Updated: 3 Oct 2005
Views: 18,440
Bookmarked: 6 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
7 votes for this Article.
Popularity: 3.03 Rating: 3.59 out of 5
0 votes, 0.0%
1
2 votes, 28.6%
2
2 votes, 28.6%
3
0 votes, 0.0%
4
3 votes, 42.9%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

 

Examine the examaple below:

 

//definition for BinaryTree.h

class BinaryTree

{

public:

      BinaryTree(void);

      ~BinaryTree(void);

      int seed;

      char* name;

      void Load(char* name);

      char* Find();

};

 

In C++ the class definitions can be separated from the implementations.  The above snippet is taken from a file BinaryTree.h which contains all definitions associated with the BinaryTree class but no implementation code.  Continuing with the example, the implementation file BinaryTree.cpp would be defined as follows.

 

#include ".\binarytree.h"

 

BinaryTree::BinaryTree(void)

{

}

 

BinaryTree::~BinaryTree(void)

{

}

 

void BinaryTree::Load(char* str){

      this->name = str;

}

 

char* BinaryTree::Find(){

      return this->name;

}

 

 

As you can see from the sample, the first line of BinaryTree.cpp explicitly declares the inclusion of the class definition found in binarytree.h.  Consequently; this completely separate file can use members defined in the later as though they were defined there. The sample is of course nonsensical but the concept nevertheless prevails; the ability to create types across multiple files.

 

It is important to note that the idea of having multiple public types in one file was so revolting to the devout object oriented programmer at one time that entire languages, Java for instance, excluded it.  However, you will find that while it is good programming practice to maintain all source code for a type in a single file, sometimes a type becomes large enough that this is an impractical constraint.

 

Partials

C# 2.0 introduces the concept of partial type definitions for classes, structs, and interfaces, as a means to break types in a program into multiple pieces to be stored in different source files for easier maintenance and development.  This approach is particularly useful when the need arises to use nested types or provide interface implementation.

 

Unlike c/C++ where an #include keyword was used to explicitly bind the contents of a header file to the implementation file, C#2.0 partial type links are inferred through the type names given.  Also, these are no interface definitions necessary in c#2.0. A sample of a partial type is given below.

 

//Binarytree_header.cs

public partial class BinaryTree

      {

            int seed;

            string name;

            public BinaryTree()

            {

            }

      }

 

 

The above example attempts to recreate the BinaryTree sample we saw earlier in the chapter using C#; notice that no include statements are needed nor are any function prototypes defined. Further implementation of this class may now be defined in other files, for instance a BinaryTree_Load.cs file may be created which produces more BinaryTree functionality.

 

//BinaryTree_load.cs: Load functionality

      public partial class BinaryTree

      {

            public void Load(string name)

            {

                  this.name = name;

            }

           

      }

 

We can also create another file BinaryTree_find.cs which would have all the functionality associated with finding an element in the tree.

 

//BinaryTree_find.cs: find functionality

      public partial class BinaryTree

      {

            public string Find()

            {

                  return name;

            }

      }

 

As you can see from the above samples, the new type modifier, partial, is used when defining a type in multiple parts. It indicates that additional parts may exist elsewhere.  I stress the word may because each partial type declaration is autonomous.  The example below illustrates this by creating a partial type declaration that includes no other parts.

 

public partial class BinaryTree

      {

 

            int x;

 

           

      }

 

 

The code above will compile successfully. Adding another part to the type will not change this successful compilation.

 

public partial class BinaryTree

      {

            int y;

      }

 

Finally, a constructor can be added to instantiate the two variables defined in both parts of the type.  A sample showing all three parts is highlighted below.

 

public partial class BinaryTree

      {

            int x;           

      }

 

      public partial class BinaryTree

      {

            int y;

      }

 

      public partial class BinaryTree

      {

            public BinaryTree()

            {

                  this.x = 100;

                  this.y = 100;

            }

      }

 

 

Each partial type declaration must include the partial modifier and must be declared within the same namespace as the other parts.

 

Compiling the code above will produce the following type definition.

 

public partial class BinaryTree

      {

            int x;

            int y;

            public BinaryTree()

            {

                  this.x = 100;

                  this.y = 100;

            }

      }

 

If we compiled the BinaryTree sample from the first section it would produce a type definition similar to the one depicted below.

 

 

 

public class BinaryTree

      {

            int seed;

            string name;

            public BinaryTree()

            {

            }

 

            public void Load(string name)

            {

                  this.name = name;

            }

 

            public string Find()

            {

                  return name;

            }

      }

 

It is important to note that partial types DO NOT allow already compiled types to be extended.  This is because all parts of a partial type must be compiled together such that the parts can be merged at compile-time to produce an actual type; as discussed in the example above.  The partial modifier, if it appears, must appear immediately before either class, struct, or interface keywords, but is not itself a keyword.

 

 

Nested Partials

Examine the three types defined in the sample below.

 

public struct DataSource

      {

            public string connectionString;

      }

 

      public class DataAccessLayer

      {

            DataSourceCollection sources;

            public DataAccessLayer()

            {

                  sources = new DataSourceCollection();

            }

 

            public DataSourceCollection DataSources

            {

                  get

                  {

                        return sources;

                  }

            }

 

            public class DataSourceCollection

            {

                  Hashtable items;             

                  public DataSourceCollection()

                  {

                        items = new Hashtable();

                  }

 

                  public DataSource this[string dataSourceName]

                  {

                        get

                        {

                              return (DataSource )items[dataSourceName];

                        }

                  }

            }

      }

 

The snippet defines a struct DataSource of which the type DataSourceCollection is an aggregate through the items Hashtable.  This type DataSourceCollection also happens to be a nested type of the DataAccessLayer type.  Using partial types in this scenario, the nested type DataSourceCollection may also be declared in multiple parts by using the partial modifier.  Hence DataAccessLayer may also be defined as follows.

 

public class DataAccessLayer

      {

            DataSourceCollection sources;

            public DataAccessLayer()

            {

                  sources = new DataSourceCollection();

            }

 

            public DataSourceCollection DataSources

            {

                  get

                  {

                        return sources;

                  }

            }

 

            public partial class DataSourceCollection

            {

                  Hashtable items;             

                  public DataSourceCollection()

                  {

                        items = new Hashtable();

                  }

 

                 

            }

 

            public partial class DataSourceCollection

            {

                  public DataSource this[string dataSourceName]

                  {

                        get

                        {

                              return (DataSource)items[dataSourceName];

                        }

                  }

            }

 

           

      }

 

This would not make to much sense though, since the type DataSourceCollection is only qualified through its container DataAccessLayer there is no way to specify a part outside of the DataSourceCollection type definition.  A more optimal approach is to define either the containing type as partial with the contained type as not.

 

public partial class DataAccessLayer

      {

            DataSourceCollection sources;

            public DataAccessLayer()

            {

                  sources = new DataSourceCollection();

            }

 

            public DataSourceCollection DataSources

            {

                  get

                  {

                        return sources;

                  }

            }    

           

      }

 

      public partial class DataAccessLayer

      {

            public class DataSourceCollection

            {

                  Hashtable items;

                  public DataSourceCollection()

                  {

                        items = new Hashtable();

                  }

 

                  public DataSource this[string dataSourceName]

                  {

                        get

                        {

                              return (DataSource)items[dataSourceName];

                        }

                  }

            }

      }

 

Or to define both containing and contained parts as partial.

 

public partial class DataAccessLayer

      {

            DataSourceCollection sources;

            public DataAccessLayer()

            {

                  sources = new DataSourceCollection();

            }

 

            public DataSourceCollection DataSources

            {

                  get

                  {

                        return sources;

                  }

            }          

      }

 

      public partial class DataAccessLayer

      {

            public partial class DataSourceCollection

            {

                  Hashtable items;

                  public DataSourceCollection()

                  {

                        items = new Hashtable();

                  }                

            }

      }

 

      public partial class DataAccessLayer

      {

            public partial class DataSourceCollection

            {

                  public DataSource this[string dataSourceName]

                  {

                        get

                        {

                              return (DataSource)items[dataSourceName];

                        }

                  }

            }

      }

 

The first example shows an easy solution to defining nested types.  With the power of partial classes, nested types can be completely isolated to their own files.  DataAccessLayer is defined as partial so that the nested type DataSourceCollection can be defined in a separate part.  The second example is probably more of a telling of the dangers of partial types when used for no good reason.  Here we see that both the containing type DataAccessLayer and the contained type DataSourceCollection are defined as partial.  Hence the the definitions can be broken into multiple parts.  Please practice caution when using partial types, particularly with regard to nested types.  You will find that in a complex program it will be almost impossible to determine what the full definition of any one type is once it is defined as partial.

 

Partial type attributes

Attributes on partial types are combined in an unspecified order so that the appear to all be defined on the given type.  In the preceding section we defined a type DataAccessLayer as follows.

 

public partial class DataAccessLayer

      {

            DataSourceCollection sources;

            public DataAccessLayer()

            {

                  sources = new DataSourceCollection();

            }

 

            public DataSourceCollection DataSources

            {

                  get

                  {

                        return sources;

                  }

            }    

           

      }

 

      public partial class DataAccessLayer

      {

            public class DataSourceCollection

            {

                  Hashtable items;

                  public DataSourceCollection()

                  {

                        items = new Hashtable();

                  }

 

                  public DataSource this[string dataSourceName]

                  {

                        get

                        {

                              return (DataSource)items[dataSourceName];

                        }

                  }

            }