Indexer Methods:
By defining an
indexer method to your custom classes and structures you may give indexer capability, that behaves just like a standard array. In another word Indexers allow you to access items in an array like fashion. Let's add indexer support to ShepeCollection type developed in
Generic chapter:
public class ShapeCollection : IEnumerable
{
//declare a private ArrayList
private ArrayList shape_array = new ArrayList();
public Shape this[int index]
{
get { return (Shape)shape_array[index]; }
set { shape_array.Insert(index, value); }
}
// ...
}
As you see apart from
this keyword is just like C#
property declaration. You can use above class in array like fashion as follow:
static void Main(string[] args)
{
ShapeCollection shapes = new ShapeCollection();
shapes[0] = new Shape();
shapes[1] = new Shape();
//..
}
It is always better for
custom collection to support
indexer methods in order to be able to
integrate well into the
fabric of the .NET base class libraries.
Indexer example for string values:
private Dictionary<string, Shape> shape_dictionary =
new Dictionary<string,Shape>();
//returns a shape based on a string indexer.
public Shape this[string name]
{
get { return (Shape)shape_dictionary[name]; }
set { shape_dictionary[name] = value; }
}
Here is another example for Multiple Dimensions:
private int[,] TowDArray = new int[10, 10];
public int this[int row, int column]
{ /*...*/ }
Indexer example for interface:
public interface IStringContainer
{
//an indexer that returns strings based on a numerical index
string this[int index] { get; set; }
}
Operator Overloading:
C# provides the
operator keyword, which you can use only in conjunction with
static methods.
- "like" operators (i.e. < and >, <= and => , == and !=) are overloaded together.
- [] , () and shorthand assignment operators (+=, -= ,*=, /=, %=, &=, |=, ^=, <<=, >>=) cannot be overloaded.
Keep in mind, the
shorthand assignment operators are
automatically simulated if a type overloads the related binary operator. Let's go through a complete example:
public class Point : IComparable
{
public int X { get; set; }
public int Y { get; set; }
public Point(int xPos, int yPos)
{
X = xPos;
Y = yPos;
}
public override string ToString()
{
return string.Format("[{0}, {1}]", this.X, this.Y);
}
//Binary ops
// overloaded operator +
public static Point operator +(Point p1, Point p2)
{ return new Point(p1.X + p2.X, p1.Y + p2.Y); }
// overloaded operator -
public static Point operator -(Point p1, Point p2)
{ return new Point(p1.X - p2.X, p1.Y - p2.Y); }
public static Point operator +(Point p1, int change)
{
return new Point(p1.X + change, p1.Y + change);
}
public static Point operator +(int change, Point p1)
{
return new Point(p1.X + change, p1.Y + change);
}
//Unary ops
// Add 1 to the X/Y values incoming Point.
public static Point operator ++(Point p1)
{ return new Point(p1.X + 1, p1.Y + 1); }
// Subtract 1 from the X/Y values incoming Point.
public static Point operator --(Point p1)
{ return new Point(p1.X - 1, p1.Y - 1); }
//Equality logic
public override bool Equals(object o)
{
return o.ToString() == this.ToString();
}
public override int GetHashCode()
{ return this.ToString().GetHashCode(); }
// Now let's overload the == and != operators.
public static bool operator ==(Point p1, Point p2)
{ return p1.Equals(p2); }
public static bool operator !=(Point p1, Point p2)
{ return !p1.Equals(p2); }
//Compare ops
public int CompareTo(object obj)
{
if (obj is Point)
{
Point p = (Point)obj;
if (this.X > p.X && this.Y > p.Y)
return 1;
if (this.X < p.X && this.Y < p.Y)
return -1;
else
return 0;
}
else
throw new ArgumentException();
}
public static bool operator <(Point p1, Point p2)
{ return (p1.CompareTo(p2) < 0); }
public static bool operator >(Point p1, Point p2)
{ return (p1.CompareTo(p2) > 0); }
public static bool operator <=(Point p1, Point p2)
{ return (p1.CompareTo(p2) <= 0); }
public static bool operator >=(Point p1, Point p2)
{ return (p1.CompareTo(p2) >= 0); }
}
And here is Main methods to illustrate the usage of above example:
static void Main(string[] args)
{
Point ptOne = new Point(100, 100);
Point ptTwo = new Point(40, 40);
Console.WriteLine("ptOne = {0}", ptOne);
Console.WriteLine("ptTwo = {0}", ptTwo);
// Add the points to make a bigger point?
//Pseudo-code: Point.operator+ (ptOne, ptTwo)
Console.WriteLine("ptOne + ptTwo: {0} ", ptOne + ptTwo);
// Subtract the points to make a smaller point?
Console.WriteLine("ptOne - ptTwo: {0} ", ptOne - ptTwo);
// Freebie +=
Point ptThree = new Point(90, 5);
Console.WriteLine("ptThree = {0}", ptThree);
Console.WriteLine("ptThree += ptTwo: {0}", ptThree += ptTwo);
// Freebie -=
Point ptFour = new Point(0, 500);
Console.WriteLine("ptFour = {0}", ptFour);
Console.WriteLine("ptFour -= ptThree: {0}", ptFour -= ptThree);
// Applying the ++ and -- unary operators to a Point.
Point ptFive = new Point(1, 1);
Console.WriteLine("++ptFive = {0}", ++ptFive); // [2, 2]
Console.WriteLine("--ptFive = {0}", --ptFive); // [1, 1]
// Apply same operators as post increment/decrement.
Point ptSix = new Point(20, 20);
Console.WriteLine("ptSix++ = {0}", ptSix++); // [20, 20]
Console.WriteLine("ptSix-- = {0}", ptSix--); // [21, 21]
Console.WriteLine("ptOne == ptTwo : {0}", ptOne == ptTwo);
Console.WriteLine("ptOne != ptTwo : {0}", ptOne != ptTwo);
Console.WriteLine("ptOne < ptTwo : {0}", ptOne < ptTwo);
Console.WriteLine("ptOne > ptTwo : {0}", ptOne > ptTwo);
Console.ReadLine();
}
 |
| operator overloading example output |
Custom Type Conversion:
Explicit casting doesn't work when types are not related by classical inheritance. However C# let you build
custom conversion routines that allow your types to respond to the
() casting operator.
//type can explicitly convert to another type
public static explicit operator CurrentType(TypeYouWantToConvertToCurrentType new_type)
{
CurrentType c = new CurrentType();
//code for conversion
return c;
}
- You cannot define an implicit and explicit conversion routine
- However when a type defines an implicit conversion routine you can make use of the explicit cast syntax
//type can implicitly convert to another type
public static implicit operator CurrentType(TypeYouWantToConvertToCurrentType new_type)
{
CurrentType c = new CurrentType();
//code for conversion
return c;
}
Extension Methods:
Allow you to
tack on new functionality to
precompiled files on the
fly without needing to directly update the file being extended. As you now the type definition is
final once a type defined and complied into a
.NET assembly. The only way to add new member, update member, or remove members is to
recode and
recompile the code base onto an updated assembly (or using the
System.Reflection.Emit namespace to
dynamically reshape a compiled type in
memory).
- cannot modify the original type declaration, it can just inject new functionality.
- must be defined within a static.
- first parameter always get the marked by this keyword modifier
- can be called from correct instance in memory or statically via the defining static class.
- always the first parameter of an extension method represents the type being extended.
As an example lets create MyExtnensions static class and add ReversDigits Extension method for integers:
static class MyExtensions
{
// This method allows any integer to reverse its digits.
public static int ReverseDigits(this int i)
{
// Translate int into a string, and then
// get all the characters.
char[] digits = i.ToString().ToCharArray();
// Now reverse items in the array.
Array.Reverse(digits);
// Put back into string.
string newDigits = new string(digits);
// Finally, return the modified string back as an int.
return int.Parse(newDigits);
}
}
you may call the above extension method through an integer like below:
int myInt = 113344;
myInt.
 |
| IntelliSense of extension method |
Calling an extension method from an object is just some
smoke-and-mirrors effect provided by the compiler.
Extension methods are
limited to the
namespace that
define them or the namespaces that
import them. Keep in mind when you extend an
interface with new members, you
must also supply an
implementation of these members.
Partial Methods:
It will allow you to prototype a method in one file, yet implement it in another file.
- Can only be defined in partial class.
- Must Return void.
- Cannot have arguments with the out modifier.
- May or may not emitted into the compiled assembly.
In the following example there are tow files "Shape.cs" and ShapeImp.cs" which contains the shape class and Draw method have been implemented using partial methods:
//file: "Shape.cs"
partial class Shape
{
//A "lightweight" event handler.
partial void OnDraw();
public void myDraw()
{
Draw();
}
}
//file: "ShapeImp.cs"
partial class Shape
{
partial void OnDraw()
{
Console.WriteLine("Draw called from \"ShapeImp.cs\" file");
}
}
When a method is defined with the
partial keyword, the
compiler will
determine if it should be emitted into the assembly based on whether the method has a method
body or is simply an empty signature.
We can say C# partial methods are a
strongly typed version of
conditional code compilation (via the #if, #elif, #else, and #endif preprocessor directives). However partial method will be completely
ignored during the compilation cycle. By marking a method with the
partial modifier, other
class builders have the
option of providing implementation details if they so choose. Which is much
cleaner solution than using preprocessor directives, supplying "dummy"implementations to virtual methods, or throwing NotImplementedException objects.
Light weight events is one of the most common use of this syntax which allows class designers to provide
method hooks, similar to event handlers, that developers may choose to implement or not. (usually with
On prefix).
In above example if any calls buileders wish to be informed when the myDraw() method has been called, they can provide an implementation of the OnDraw() method. If they do not care, they simply do nothing.
Anonymous Types:
Is useful when you need a
temporary type in order to model a set of
encapsulated data points without any associated methods, events, or other custom functionality. Anonymous types are marked with
var keyword in conjunction with
object initialization syntax. Consider the following example:
//make an anonymous type representing a task
var myTask = new {name ="Simple Task", load = 1000};
Console.WriteLine("This is \"{0}\" task with \"{1}\" load", myTask.name, myTask.load);
Console.WriteLine("The type is {0}", myTask.GetType().Name);
Console.WriteLine(myTask.ToString());
 |
| output of anonymous type |
MyTask
must be
implicitly typed, as we are not modeling the concept of a Task using a strongly typed class definition. Compiler will
autogenerate a uniquely named class which is
not visible from C#. Also we
have to specify the set of properties (using object initialization syntax) that model the data we are attempting to encapsulate. Once defined, these values can then be obtained using standard C# property invocation syntax.
 |
| compiler-generated class represent (ildasm.exe) |
Each
name/value pair defined using the object initialization syntax is mapped to an identically named
read-only property and corresponding private read-only backing field. Good to know that Equals(), GetHashCode(), and ToString() is already overridden.
ToString() implementation builds a string from each
name/value pair.
GetHashCode() implementation
computers a hash value using eash anonymous type's member variable as
input to the
System.Collections.Generic.EqualityComparer<T> (tow anonymous types have same hash value if they have same set of properties and values). Consider following example for Equal() method:
// Make 2 anonymous classes with identical name/value pairs.
var firstPet = new { Type = "Cat", Color = "Wight", Age = 55 };
var secondPet = new { Type = "Cat", Color = "Wight", Age = 55 };
// Equals() --> test value type
if (firstPet.Equals(secondPet))
Console.WriteLine("Same object!");
else
Console.WriteLine("Different object!");
// == --> test reference type
if (firstPet == secondPet)
Console.WriteLine("Same object!");
else
Console.WriteLine("Different object!");
//objects type
if (firstPet.GetType().Name == secondPet.GetType().Name)
Console.WriteLine("Same type!");
else
Console.WriteLine("Different types!");
 |
| Equal example output |
Keep in mind Anonymous types
cannot support
events,
custom methods,
custom operators, or
custom overrides. They are
always implicitly sealed and created using
default constructor.
Here is another example demonstrating nested anonymous types:
//Make an anonymous type that is
composed of another
var myProduct = new
{
ProductName = "Lumia 800",
Manufacturer = new { Name = "Nokia", Contact = "link.nokia.com/support" },
Price =
1650,
};
Pointer Types:
To
bypass the
CLR's memory-management and take matters into our own hand and .NET run-time has absolutely no clue of your intentions. This is helpful for some
optimization purpose or calling methods of
C-based .dll or
COM server that demand pointer types as parameter. In order to inform the compiler you need to set the following flag: project's Properties-->Build tab --> Allow unsafe code.(below screen shot)
 |
| Setting the unsafe flag |
After setting unsafe flag you are allowed to use unsafe syntax in your code like the following examples:
unsafe
{
//work with pointer type unsafe block
}
//unsafe structure
public unsafe struct Node
{
public int Value;
public Node* Left;
public Node*
Right;
}
Methods (static or not-static) can marked as unsafe as well. In C# the
*operator is
applied to the
underlying type only, not as a prefix to each pointer variable name.Consider following example:
//Wrong !!
int *p1, *p2;
//Right
int* p1,p2;
stackalloc:
In order to
allocate memory directly from the
call stack ( not subject to .NET garbage collection). Therefore the allocated memory is cleaned up as soon as the allocating method has returned. Let's go through an example:
unsafe static void UnsafeStacAlloc()
{
char* p = stackalloc char[256];
for(int k = 0 ; k
< 256; k++)
p[k]
= (char) k;
}
fixed Keyword:
In order to set a pointer to a
manged type and "pin" that variable during the execution of the code. In fact compiler do
not allow you to set a pointer to a managed variable
except in a fixed statement. In another word fixed keyword help you to lock a reference variable in memory, such that its address remains constant for the duration of the statement (or scope block). Here is an example:
class PointClass
{
public int x;
public int y;
}
PointClass pt = new
PointClass();
pt.x = 5;
pt.y = 6;
fixed (int* p = &pt.x)
{
//use int* variable!
}
sizeof Keyword:
Used to obtain the
size in bytes of a value type (never a reference type). As sizeof will evaluate the number of bytes for any
System.ValueType-derived entity, you can obtain the size of custom structures as well. Example:
unsafe
{
Console.WriteLine("The
size of short is {0}.", sizeof(Node));
}
Keep in mind when System.ValueType-derived doesn't have
predefined size you
must put it in unsafe context.
Reference: Pro C# 2010 and the .NET 4 Platform by Andrew Troelsen.