20. Assemblies, Namespaces & Access Levels
In C#, you can organize the components of your source-code (classes, structs, delegates, enums, etc.) into files, namespaces & assemblies.
Namespaces are nothing but syntactic sugar for long class names. For instance, rather than calling a class Genamics.WinForms.Grid you can declare the class as Grid and enclose the class with:
namespace Genamics.WinForms {
public class Grid {
....
}
}
For classes which use Grid, you can import it using the "using" keyword instead of referring to its full class name Genamics.WinForms.Grid.
Assemblies are .exes or .dlls generated from compiling a project of files. The .NET runtime uses the configurable attributes and versioning rules built into assemblies to greatly simplify deployment - no more hacking the registry - just copy the assembly into a directory and it goes. Assemblies also form a type-boundary to deal with type-name collisions, to the extent that multiple versions of an assembly can co-exist in the same process. Each file can contain multiple classes and multiple namespaces. A namespace may also be spread across several assemblies, so there is high level of freedom with this system.
There are five access levels in C#, private, internal, protected, internal protected, and public. Private and public have the same meanings as in Java, noting that in C# the default access level is private, rather than package. Internal access is scoped to assemblies rather than namespaces (which would be more analogous to Java). Internal protected access is equivalent to Java's protected access, and protected access is equivalent to Java's private protected access made obsolete in Java some time ago.
21. Pointer Arithmetic
Pointer arithmetic can be performed in C# within methods marked with the unsafe modifier. When pointers point to garbage collected objects, the compiler enforces the use of the fixed word to pin the object. This is because garbage collectors rely on moving objects around to reclaim memory, but if this happens when you're dealing with raw pointers you'll be pointing at garbage. The choice of the word "unsafe" I believe is well chosen since it discourages developers from using pointers unless they really need to.
22. Rectangular Arrays
C# allows both jagged and rectangular arrays to be created. Jagged arrays are pretty much the same as Java arrays. Rectangular arrays allow a more efficient and accurate representation for certain problems. An example of such an array would be:
int [,,] array = new int [3, 4, 5]; // creates 1 array
int [1,1,1] = 5;
Using jagged arrays:
int [][][] array = new int [3][4][5]; // creates 1+3+12=16 arrays
int [1][1][1] = 5;
In combination with structs, C# can provide a level of efficiency making it a good choice for areas such as graphics and mathematics.
23. Constructors and Destructors
You can specify optional constructor parameters:
class Test
{
public Test () : this (0, null) {}
public Test (int x, object o) {
}
}
You can specify static constructors:
class Test
{
static int[] ascendingArray = new int [100];
static Test () {
for (int i = 0; i < ascendingArray.Length; i++)
ascendingArray [i] = i;
}
}
Destructors are specified with the C++ naming convention using the ~ symbol. Destructors can only be made for references types and not value types, and cannot be overridden. A destructor can not be explicitly called, since it doesn't make much sense to do so when an object's lifetime is managed by the garbage collector. Each destructor in an object's hierarchy (from the most derived to the least derived) is called before the memory for the object is reclaimed.
Despite the naming similarity with C++, destructors in C# are much more like Java finalizers. This is because they are called by the garbage collector rather than explicitly called by the programmer. Furthermore, like Java finalizers, they cannot be guaranteed to be called in all circumstances (this always shocks everyone when they first discover this). If you are used to programming with deterministic finalization (you know when an object's destructor is called), then you have to adapt to a different model when moving to Java or C#. The model Microsoft recommends and implements throughout the .NET framework is the dispose pattern, whereby you define a dispose() method to for classes which manage foreign resources such as graphics handles or database connections. For distributed programming, the .NET framework provides a leased based model to improve upon DCOM reference counting.
Previous
Next
|