Wednesday, April 30, 2008

Partial Classes-1

Partial classes

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 fromthe implementations. The above snippet is takenfrom a file BinaryTree.h which contains all definitions associated with theBinaryTree class but no implementation code. Continuing with the example, the implementation file BinaryTree.cpp would bedefined as follows.

#include ".\binarytree.h"

BinaryTree::BinaryTree(void)
{
}

BinaryTree::~BinaryTree(void)
{
}

void BinaryTree::Load(char* str){
this->name =str;
}

char* BinaryTree::Find(){
returnthis->name;
}


As you can see from the sample, the first line ofBinaryTree.cpp explicitly declares the inclusion of the class definition foundin binarytree.h. Consequently; this completelyseparate file can use members defined in the later as though they were definedthere. The sample is of course nonsensical but the concept neverthelessprevails; the ability to create types across multiple files.

It is important to note that the idea of havingmultiple public types in one file was so revolting to the devout object orientedprogrammer at one time that entire languages, Java for instance, excludedit. However, you will find that while it isgood programming practice to maintain all source code for a type in a singlefile, sometimes a type becomes large enough that this is an impracticalconstraint.

Partials
C# 2.0 introduces the concept of partial typedefinitions for classes, structs, and interfaces, as a means to break types in aprogram into multiple pieces to be stored in different source files for easiermaintenance and development. This approach isparticularly useful when the need arises to use nested types or provideinterface implementation.

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

//Binarytree_header.cs
public partial class BinaryTree
{
intseed;
stringname;
publicBinaryTree()
{
}
}


The above example attempts to recreate theBinaryTree sample we saw earlier in the chapter using C#; notice that no includestatements are needed nor are any function prototypes defined. Furtherimplementation of this class may now be defined in other files, for instance aBinaryTree_Load.cs file may be created which produces more BinaryTreefunctionality.

//BinaryTree_load.cs: Loadfunctionality
public partial classBinaryTree
{
public voidLoad(string name)
{
this.name= name;
}

}

We can also create another file BinaryTree_find.cswhich would have all the functionality associated with finding an element in thetree.

//BinaryTree_find.cs: findfunctionality
public partial classBinaryTree
{
public stringFind()
{
returnname;
}
}

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

public partial class BinaryTree
{

intx;


}


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

public partial class BinaryTree
{
inty;
}

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

public partial class BinaryTree
{
int x;
}

public partial classBinaryTree
{
inty;
}

public partial classBinaryTree
{
publicBinaryTree()
{
this.x =100;
this.y =100;
}
}


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

Compiling the code above will produce thefollowing type definition.

public partial class BinaryTree
{
intx;
inty;
publicBinaryTree()
{
this.x =100;
this.y =100;
}
}

If we compiled the BinaryTree sample from thefirst section it would produce a type definition similar to the one depictedbelow.



public class BinaryTree
{
intseed;
stringname;
publicBinaryTree()
{
}

public voidLoad(string name)
{
this.name= name;
}

public stringFind()
{
returnname;
}
}

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


NestedPartials
Examine the three types defined in the samplebelow.

public struct DataSource
{
public stringconnectionString;
}

public classDataAccessLayer
{
DataSourceCollection sources;
publicDataAccessLayer()
{
sources =new DataSourceCollection();
}

publicDataSourceCollection DataSources
{
get
{
return sources;
}
}

public classDataSourceCollection
{
Hashtableitems;
publicDataSourceCollection()
{
items = new Hashtable();
}

publicDataSource this[string dataSourceName]
{
get
{
return (DataSource)items[dataSourceName];
}
}
}
}

The snippet defines a struct DataSource of whichthe type DataSourceCollection is an aggregate through the items Hashtable. This type DataSourceCollection also happens to be anested type of the DataAccessLayer type. Usingpartial types in this scenario, the nested type DataSourceCollection may also bedeclared in multiple parts by using the partial modifier. Hence DataAccessLayer may also be defined asfollows.

public class DataAccessLayer
{
DataSourceCollection sources;
publicDataAccessLayer()
{
sources =new DataSourceCollection();
}

publicDataSourceCollection DataSources
{
get
{
return sources;
}
}

public partialclass DataSourceCollection
{
Hashtableitems;
publicDataSourceCollection()
{
items = new Hashtable();
}


}

public partialclass DataSourceCollection
{
publicDataSource this[string dataSourceName]
{
get
{
return(DataSource)items[dataSourceName];
}
}
}


}

This would not make to much sense though, sincethe type DataSourceCollection is only qualified through its containerDataAccessLayer there is no way to specify a part outside of theDataSourceCollection type definition. A moreoptimal approach is to define either the containing type as partial with thecontained type as not.

public partial class DataAccessLayer
{
DataSourceCollection sources;
publicDataAccessLayer()
{
sources =new DataSourceCollection();
}

publicDataSourceCollection DataSources
{
get
{
return sources;
}
}

}

public partial classDataAccessLayer
{
public classDataSourceCollection
{
Hashtableitems;
publicDataSourceCollection()
{
items = new Hashtable();
}

publicDataSource this[string dataSourceName]
{
get
{
return(DataSource)items[dataSourceName];
}
}
}
}

Or to define both containing and contained partsas partial.

public partial class DataAccessLayer
{
DataSourceCollection sources;
publicDataAccessLayer()
{
sources =new DataSourceCollection();
}

publicDataSourceCollection DataSources
{
get
{
return sources;
}
}
}

public partial classDataAccessLayer
{
public partialclass DataSourceCollection
{
Hashtableitems;
publicDataSourceCollection()
{
items = new Hashtable();
}
}
}

public partial classDataAccessLayer
{
public partialclass DataSourceCollection
{
publicDataSource this[string dataSourceName]
{
get
{
return(DataSource)items[dataSourceName];
}
}
}
}


NEXT>>>

No comments: