Some salient features of C#
This article illustrates some of the salient features of C# programming language such as Dictionary.TryGetValue() method, ??, better use of automatic properties, as keyword, object initializers, default keyword, manipulating memory data, use of DEBUG pre-processor directive and many more. I have also mentioned the reason for using these features includeing the sample code. One can leverage these features to improve C# code-writing and deliver more optimized code.
Some good features of C#
1) Dictionary.TryGetValue(K key, out V value)
in general we use:
if(dictionary.ContainsKey(key))
{
value = dictionary[key];
... Statement A...
... Statement B...
}
instead we can use this optimized way:
if(dictionary.TryGetValue(key, out value))
{
... Statement A...
... Statement B...
}
2) ??
This is used to return a default value if some identifier has a null value or reference
in general we use:
int? x = 53;
int y = x;
what if, x has a null value?
use following approach:
int? x = null;
int y = x ?? -1;
3) Flagging the numbers
Different numeric data types can be flagged with some Alphabets to identify their nature.
To identify the nature of numbers we can flag them as under:
double dbl = 30D;
float flt = 35123.5123F;
decimal dcm = 45000.75M; // M cams from "M=Money=Decimal" of old VB
long lng = 12313123123L;
Unsigned long unsl = 18127361231172UL;
Unsigned int unsi = 1812736172U;
4) use of "automatic properties"
in general we use:
private string _name;
...
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
Instead we can now use:
public string Name { get; set;}
5) Scope on auto properties
in general we create automatic properties as follow:
public int EmployeeCode
{
get;
set;
}
What if we dont want to set property externally?
We can set the scope of public auto properties...
public int EmployeeCode
{
get;
private set;
}
6) using "as" keyword
in general we use:
EmployInfo emp = (EmployInfo)person;
What-if person object does not contain the object of the type EmployInfo?
1st approach one will throw exception
To handle this, use following approach:
EmployInfo emp = person as EmployInfo;
2nd approach will return null
7) Object Initializers
in general we use:
Employee emp = new Employee();
emp.Name = "John Smith";
emp.StartDate = DateTime.Now();
What-if there are so many properties to set in Employee class
use this Object Initializer:
Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()} ;
Simple add new property initialization in {..., ..., ...}
8) "default" keyword for generics
in general we use:
T obj = null;
Instead follow this:
T obj = default(T);
2nd approach will assign default value to obj as per the Type of T..
e.g.
null --- if reference type
0 --- if integer type
false --- if boolean, etc.
9) "unions" of memory locations ... Not recommended but one should know this.
Without using pointers or unsafe mode you can manipulate memory space by class members.
Give a try to following code snippet with a console application:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("\n\nSTATE # 1 (Before manipulation)...\n");
Test test = new Test();
Console.WriteLine(String.Format(" number32 = {0}, number16 = {1}", test.number32, test.number16));
Console.WriteLine(String.Format(" A:{0}, B:{1}, C:{2}, D:{3}, E:{4}, F:{5}, G:{6}, H:{7}", test.A, test.B, test.C, test.D, test.E, test.F, test.G, test.H));
Console.WriteLine("\n\nSTATE # 2 (After this manipulation... set maximum integer value in number32)...\n");
test = new Test { number32 = int.MaxValue };
Console.WriteLine(String.Format(" number32 = {0}, number16 = {1}", test.number32, test.number16));
Console.WriteLine(String.Format(" A:{0}, B:{1}, C:{2}, D:{3}, E:{4}, F:{5}, G:{6}, H:{7}", test.A, test.B, test.C, test.D, test.E, test.F, test.G, test.H));
Console.WriteLine("\n\nSTATE # 3 (More manipulation... set 0 in C and D)...\n");
test.B = test.C = test.D = 10;
test.C = 20;
Console.WriteLine(String.Format(" number32 = {0}, number16 = {1}", test.number32, test.number16));
Console.WriteLine(String.Format(" A:{0}, B:{1}, C:{2}, D:{3}, E:{4}, F:{5}, G:{6}, H:{7}", test.A, test.B, test.C, test.D, test.E, test.F, test.G, test.H));
Console.WriteLine("\n\nSTATE # 4 (More manipulation... set 50 in C and 60 in D)...\n");
test.C = 50;
test.D = 60;
Console.WriteLine(String.Format(" number32 = {0}, number16 = {1}", test.number32, test.number16));
Console.WriteLine(String.Format(" A:{0}, B:{1}, C:{2}, D:{3}, E:{4}, F:{5}, G:{6}, H:{7}", test.A, test.B, test.C, test.D, test.E, test.F, test.G, test.H));
Console.WriteLine("\n\nSTATE # 5 (More manipulation... set 64 in A, B, C, D, E, F, G, H each)...\n");
test.A = test.B = test.C = test.D = test.E = test.F = test.G = test.H = 64;
Console.WriteLine(String.Format(" number32 = {0}, number16 = {1}", test.number32, test.number16));
Console.WriteLine(String.Format(" A:{0}, B:{1}, C:{2}, D:{3}, E:{4}, F:{5}, G:{6}, H:{7}", test.A, test.B, test.C, test.D, test.E, test.F, test.G, test.H));
Console.ReadKey();
}
}
[StructLayout(LayoutKind.Explicit)]
public class Test
{
[FieldOffset(0)]
public byte A; // 1 byre starts from offset 0 in memory
[FieldOffset(1)]
public byte B; // 1 byte starts from offset 1in memory
[FieldOffset(2)]
public byte C; // 1 byte starts from offset 2 in memory
[FieldOffset(3)]
public byte D; // 1 byte starts from offset 3 in memory
[FieldOffset(0)]
public Int32 number32; // 4 bytes starts from offset 0 in memory > 1+1+1+1 = 4 bytes
[FieldOffset(4)]
public byte E; // 1 byre starts from offset 4 in memory
[FieldOffset(5)]
public byte F; // 1 byte starts from offset 5 in memory
[FieldOffset(6)]
public byte G; // 1 byte starts from offset 6 in memory
[FieldOffset(7)]
public byte H; // 1 byte starts from offset 7 in memory
[FieldOffset(4)]
public Int16 number16; // 2 bytes starts from offset 4 in memory > 1+1 = 2 bytes
}
}
Run the application. you may get following output...
STATE # 2 (After this manipulation... set maximum integer value in number32)...
number32 = 2147483647, number16 = 0
A:255, B:255, C:255, D:127, E:0, F:0, G:0, H:0
STATE # 3 (More manipulation... set 0 in C and D)...
number32 = 169085695, number16 = 0
A:255, B:10, C:20, D:10, E:0, F:0, G:0, H:0
STATE # 4 (More manipulation... set 50 in C and 60 in D)...
number32 = 1009912575, number16 = 0
A:255, B:10, C:50, D:60, E:0, F:0, G:0, H:0
STATE # 5 (More manipulation... set 64 in A, B, C, D, E, F, G, H each)...
number32 = 1077952576, number16 = 16448
A:64, B:64, C:64, D:64, E:64, F:64, G:64, H:64
.
This shows that that data manipulation is possible in absolute memory locations. But it is an unsafe operation hence not recommended!
10) using DEBUG pre-processor directive
you can use this directive to take more control on application execution duribng Debugging mode.
eg.
...
...
#if DEBUG
// Place piece of code3 here that will execute during debugging
// - like initializing some variables
// - calling some methods
// - creating some objects
// - writing some log-information
// etc
#endif
...
...
All these tips are taken from http://stackoverflow.com/questions/9033/hidden-features-of-c
More Tips are available on the above link.
Thanks