A Practical Example of Static Costructor in C# .NET (Singletons)
Singletons provide an excellent example of private and static constructors. A singleton is an object that appears once in the problem domain. Singletons are limited to one instance, but that instance is required. This requirement is enforced in the implementation of the singleton.
The singleton presented in this chapter has two constructors. The private constructor means that an instance cannot be created outside the class. That would require calling the constructor publicly. However, an instance can be created inside the class. The single instance of the class is exposed as a static member, which is initialized in the static constructor. The instance members of the class are accessible through the static instance. Because the static constructor is called automatically, one instance—the singleton—always exists.
A chess game is played with a single board—no more or less. This is the singleton for a chess board:
using System; namespace Examples.Singleton {
public class Chessboard
{
public static Chessboard board=null;
public string player1;
public string player2;
public string start
private Chessboard() {}
static Chessboard()
{
board=new Chessboard();
board.start=DateTime.Now.ToShortTimeString();
}
}
public class Game
{
public static void Main()
{
Chessboard game=Chessboard.board;
game.player1="Sally";
game.player2="Bob";
Console.WriteLine("{0} played {1} at {2}", game.player1, game.player2, game.start);
}
}
}
In Main, game is an alias for the ChessBoard.board singleton. The local variable is not another instance of Chessboard. The alias is simply a convenience. I preferred game.player1 to Chess-Board.board.player1.
Partial Classes in C# .NET
A partial class can span multiple source files. When compiled, the partial class is reassembled in the assembly. Each source file has a complete class declaration and body. The class declaration is preceded with the partial keyword. The class body in each source file includes only the members that the partial class is contributing to the overall type. Code generators will benefit from partial classes. Rely on the code generator or wizard to generate the core code. The developer can then extend the code without disturbing the base code. Teams of developers collaborating on an application also benefit from partial classes. The class can be parceled based on responsibility. The developer works in separate source files, which isolate changes and focus a developer on code relevant to their responsibility.
Consistency is the key to partial types:
- Each partial type is preceded with the partial keyword.
- The partial types must have the same accessibility.
- If any part is sealed, the entire class is sealed.
- If any part is abstract, the entire class is abstract.
- Inheritance at any partial type applies to the entire class.
Here is the example of a class separated into partial types:
//partial1.cs
using System;
namespace Examples.Classes
{
public partial class ZClass
{
public void CoreMethodA()
{
Console.WriteLine("ZClass.CoreA");
}
}
public class Starter
{
public static void Main()
{
ZClass obj=new ZClass();
obj.CoreMethodA();
obj.ExtendedMethodA();
}
}
}
//partial2.cs
using System;
namespace Examples.Classes
{
public partial class ZClass
{
public void ExtendedMethodA()
{
Console.WriteLine("ZClass.ExtendedA");
}
}
}
C# .NET Bitwise Enumeration
Bitwise enumeration is for mutually inclusive flags. Each member of the enumeration is assigned a unique bitwise value. Apply the flags attribute to an enumeration to specify bitwise enumeration.
Combine bitwise flags using the or operator (|). Confirm the existence of a flag with the bitwise and operator (&).
The following code demonstrates the flags attribute of an enumeration:
using System;
namespace Examples.Enumerators{
[Flags] public enum Contribution {
Pension = 0x01,
ProfitSharing = 0x02,
CreditBureau = 0x04,
SavingsPlan = 0x08,
All = Pension|ProfitSharing|CreditBureau|SavingsPlan
}
public class Employee {
private Contribution prop_contributions;
public Contribution contributions {
get {
return prop_contributions;
}
set {
prop_contributions=value;
}
}
}
public class Starter{
public static void Main(){
Employee bob = new Employee();
bob.contributions = Contribution.ProfitSharing | Contribution.CreditBureau;
if((bob.contributions & Contribution.ProfitSharing) == Contribution.ProfitSharing) {
Console.WriteLine("Bob enrolled in profit sharing");
}
}
}
}
Namespaces in C# .NET
Namespaces provide hierarchical clarity and organization of types and other members. A container of hundreds of classes, the .NET Framework Class Library (FCL) is an example of effective use of namespaces. The Framework Class Library would sacrifice clarity if planned as a single namespace with a flat hierarchy. Instead, the Framework Class Library organizes its members into numerous namespaces. System, which is the root namespace of the FCL, contains the classes ubiquitous to .NET, such as the Console class. Types related to data services are grouped in the System.Data namespace. Data services are further delineated in the System.Data.SqlClient namespace, which contains types specific to Microsoft SQL. The remaining types are organized similarly in other namespaces.
A namespace identifier must be unique within the namespace declaration space, which contains the current namespace but not a nested namespace. A nested namespace is considered a member of the containing namespace. Use the dot punctuator (.) to access members of the namespace.
A namespace at file scope, not nested within another namespace, is considered part of the compilation unit and included in the global namespace. A compilation unit is a source code file. A program partitioned into several source files has multiple compilation units—one compilation unit for each source file. Any namespace, including the global namespace, can span multiple compilation units. For example, all types defined at file scope are included into a single global namespace that spans separate source files.
The following code has two compilation units and three namespaces. Because of identical identifiers sharing the same scope, errors occur when the program is compiled.
// file1.cs
public class ClassA {
}
public class ClassB {
}
namespace NamespaceZ {
public class ClassC {
}
}
// file2.cs
public class ClassB {
}
namespace NamespaceY {
public class ClassA {
}
}
namespace NamespaceZ {
public class ClassC {
}
public class ClassD {
}
}
Compile the code into a library:
csc /t:library file1.cs file2.cs
In the preceding code, the global namespace has four members. NamespaceY and NamespaceZ are members. The classes ClassA and ClassB are also members of the global namespaces. The members span the File1.cs and File2.cs compilation units, which both contribute to the global namespace.
ClassB and ClassC are ambiguous. ClassB is ambiguous because it is defined twice in the global namespace, once in each compilation unit. ClassC is defined in the NamespaceZ namespace in both compilation units. Because NamespaceZ is one cohesive namespace, ClassC is also ambiguous.
The relationship between compilation units, the global namespace, and nonglobal namespaces are illustrated in the Figure.
Nested Namespace
A namespace can be defined within another namespace. This is known as nested namespace. Nested namespace can be defined as follows:
namespace NamspaceZ
{
namespace NamespaceY
{
…
}
}
or,
namespace NamspaceZ.NamespaceY
{
…
}
Fully Qualified Names
The using directive makes a namespace implicit. You can access members of the named namespace directly without the fully qualified name. Do you refer to members of your family by their fully qualified names or just their first names? Unless your mother is the queen, you probably refer to everyone directly by simply using their first names, for the sake of convenience. The using directive means that you can treat members of a namespace like family members.
The using directive must precede any members in the namespace where it is defined. The following code defines the namespace member ClassA. The fully qualified name is NamespaceZ.NamespaceY.ClassA. Imagine having to type that several times in a program!
using System;
namespace NamespaceZ {
namespace NamespaceY {
class ClassA {
public static void FunctionM() {
Console.WriteLine("FunctionM");
}
}
}
}
namespace Application {
class Starter {
public static void Main() {
NamespaceZ.NamespaceY.ClassA.FunctionM();
}
}
}
The following using directive makes NamespaceZ.NamespaceY implicit. Now you can directly access ClassA.
namespace Application {
using NamespaceZ.NamespaceY;
class Starter {
public static void Main() {
ClassA.FunctionM();
}
}
}
Ambiguities can occur when separate namespaces with identically named members are made implicit. When this occurs, the affected members can be assessed only with their fully qualified names.
Aliasing Namespace
The using directive can also define an alias for a namespace or type. Aliases are typically created to resolve ambiguity or as a convenience. The scope of the alias is the declaration space where it is declared. The alias must be unique within that declaration space. In this source code, an alias is created for the fully qualified name of ClassA:
namespace Application {
using A=NamespaceZ.NamespaceY.ClassA;
class Starter {
public static void Main() {
A.FunctionM();
}
}
}
In this code, A is a nickname for NamespaceZ.NamespaceY.ClassA and can be used synonymously.
Using directive statements are not cumulative and are evaluated independently.
-
Archives
- February 2009 (1)
- November 2008 (6)
- October 2008 (4)
- September 2008 (13)
- August 2008 (11)
- July 2008 (29)
- June 2008 (19)
- May 2008 (8)
-
Categories
-
RSS
Entries RSS
Comments RSS


