Every one can be a C# developer but being a good developer meant to constantly review some key topics. This page is not tutorial for c#, it is more like "did you know that...?". Topics are not exhaustive but they are essential and organized like a check-list. All scenarios source are available under CharpsMainTopics solutions.
Generics
Generics provide type safety without the overhead of multiple implementations. It offers the ability to write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations. But there are few thinks to keep in mind while using generics.
Scenario:Why does this code not compile?
public class Generic<T>
{
public T Field;
public T AddSub()
{
T i = Field + 1;
}
}
The answer is: the + operator is not defined for T. For further information, please visit:
Constraints
Scenario1:Why does this code not compile?
class CustomFactory<T>
{
public T CreateItem()
{
return new T();
}
}
The answer is : CreateItem Methode tries to instantiate a new object of type T and the compile does not now if there is a parameter-less constructor of type T so to make it work we need add new constraint to this generic class. For further information, please visit:
Scenario2:What will be printed on the Console and Why?
public static void PrintAreEqual<T>(T s, T t) where T : class
{
System.Console.WriteLine(s == t);
}
static void Main()
{
string s1 = "target";
System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
string s2 = sb.ToString();
PrintAreEqual<string>(s1, s2);
Console.Read();
}
The answer is : False. Using where T : class constraint induce that == will check reference equality and not value equality. For further information, please visit:
Scenario3:Why does this code compile?
class A { public A() { } }
struct C { }
class D<T> where T : new()
{
D<A> c1 = new D<A>();
D<C> c3 = new D<C>();
}
The answer is : struct C is a value-type, Unlike classes, structs can be instantiated without using a new operator.
Generic Collections
A generic collection enforces type safety so that no other data type can be added to it. When you retrieve an element from a generic collection, you do not have to determine its data type or convert it. The classes in the System.Collections namespace do not store elements as specifically typed objects, but as objects of type Object.
Some useful Generic collections:
- LinkedList is a general-purpose linked list that provides O(1) insertion and removal operations.
- SortedDictionary is a sorted dictionary with O(log n) insertion and retrieval operations, which makes it a useful alternative to SortedList.
- KeyedCollection is a hybrid between a list and a dictionary, which provides a way to store objects that contain their own keys.
- BlockingCollection implements a collection class with bounding and blocking functionality.
- ConcurrentBag provides fast insertion and removal of unordered elements.
For further information, please visit:
Variance
Scenario1: Why does this code not compile?
List<int> ints = new List<int>();
ints.Add(1);
ints.Add(10);
ints.Add(42);
List<object> objects = new List<object>();
objects.AddRange(ints);
The answer is : Generic collections are strongly type, event if int is convertible to object, List<int>
are not List<object>
. There is simple workarround:
AddRange<int, object>(ints, objects);
public static void AddRange<S, D>(List<S> source, List<D> destination)
where S : D
{
foreach (S sourceElement in source)
{
destination.Add(sourceElement);
}
}
A generic interface or delegate is called variant if its generic parameters are declared covariant or contravariant For further information, please visit:
Scenario2:What king of runtime exception do we get with code above?
class Instrument {
public override string ToString()
{
return "I am simple Instrument";
}
}
class Index:Instrument {
public override string ToString()
{
return "I am an Index";
}
}
class IndexOption : Index {
public override string ToString()
{
return "I am an option over Index";
}
}
class Program
{
static void Main(string[] args)
{
Index l_index = new Index();
Instrument l_instrument = l_index;
Console.WriteLine(l_instrument);
IndexOption l_indexOption = new IndexOption();
l_instrument = l_indexOption;
Console.WriteLine(l_instrument);
Instrument[] l_instruments = new Instrument[3];
l_instruments[0] = new Index();
l_instruments[1] = new Instrument();
l_instruments[2] = new IndexOption();
for (int l_i = 0; l_i < l_instruments.Length; ++l_i)
Console.WriteLine(string.Format("array[{0}]={1}:", l_i, l_instruments[l_i]));
Instrument[] l_instruments2 = new IndexOption[3];
l_instruments2[0] = new Index();
l_instruments2[1] = new Instrument();
l_instruments2[2] = new IndexOption();
for (int l_i = 0; l_i < l_instruments2.Length; ++l_i)
Console.WriteLine(string.Format("array[{0}]={1}:", l_i, l_instruments2[l_i]));
Console.Read();
}
}
The answer is : System.ArrayTypeMismatchException with theses lines:
l_instruments2[0] = new Index();
l_instruments2[1] = new Instrument();
An Index object and IndexOption are Instrument because of Implicit Conversion: covariance but when it came to array, we better not forget that covariance is not type safe. Run-time failure is due to run-time type checking when assign operation actually happen. Array methods are not covariante. In this scenario we should add extra method in order to be able to add Instrument or Index into l_instruments2 array.
For further information, please visit:
Variance in Delegates
Scenario3: What is wrong with this peace of code?
public class Instrument
{
public double Price { get; set; }
public override string ToString()
{
return "I am simple Instrument";
}
}
public class Index : Instrument
{
public override string ToString()
{
return "I am an Index";
}
}
public class IndexOption : Index
{
public double Premium { get; set; }
public override string ToString()
{
return "I am an option over Index";
}
}
class Program
{
static void Main(string[] args)
{
Action<Instrument> l_PrintNameAction = x => Console.WriteLine("Instrument Type is : {0}", x.ToString());
Action<Instrument> l_PrintPriceAction = x => Console.WriteLine("Instrument Price: {0}", x.Price);
Action<IndexOption> l_PrintPremiumAction = x => Console.WriteLine("Extra Info: {0}", x.Premium);
Delegate.Combine(l_PrintNameAction, l_PrintPriceAction, l_PrintPremiumAction).
DynamicInvoke((new IndexOption { Price = 12, Premium = 2 }));
Console.Read();
}
}
The answer is : Delegate.Combine lead to a run-time exception because The Combine method or (+) operator does not support variant delegate.
For further information, please visit:
Expressions
Just few thing to keep in mind for Expression trees -need to be compiled with Compile() method -need to be run with (parameter(s)) or () Example:
int l_result = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);
Operators
Scenario1:What will be printed on the console? and Why?
static void Main(string[] args)
{
int ten = 10;
int i2 = 2147483647 + ten;
Console.WriteLine(i2 == 2147483657);
Console.Read();
}
The answer is : False, Because 2147483647 is the max value of integer
, Here we got an overflow but there are neither compilation errors nor run-time exceptions raised.
What is happening?
The explanation is by default:
- These non-constant expressions are not checked for overflow at run time: no run-time exception are raised
- An expression that contains only constant values causes a compiler error if the expression produces a value that is outside the range of the destination type: no compilation errors.
Unless you want this behavior, this is a kind of hidden bug. To avoid it we got two choices:
- compile with /checked
- use the key word checked
// Checked expression.
Console.WriteLine(checked(2147483647 + ten));
// Checked block.
checked
{
int i3 = 2147483647 + ten;
Console.WriteLine(i3);
}
For further information, please visit:
Query Expressions
Arrays and Collections
Arrays
Scenario1:Is this assign operation possible? and Why?
static void Main(string[] args)
{
object[] objectArray = new int[] { 1, 2, 4 };
}
The answer is : False, Because of strong type checking
Scenario2:** Does this cast operation really happen? So What is Wrong?**
static void Main(string[] args)
{
int[] objectArray = new int[] { 1, 2, 4 };
Array myArray = (Array)objectArray;
System.Console.WriteLine(myArray.GetType() == typeof(Array));
}
The answer is : False, GetType() return exact runtime type of the current instance. int[] is a reference type, pointing to int[] type. Casting int[] to Array is just about reference casting but not pointed values. The following statement return True
Console.WriteLine(myArray.GetType().BaseType == typeof(Array));
if we want to cast, the actually pointed of objectArray to Array, we could use Cast extension method of System.Linq for instance.
For further information, please visit:
Array Usage
Scenario1: do you think private member jsonSepartors| array is safe from modification?
public enum ESperatorType { JSON, CSV }
public class Seprators
{
private static char[] jsonSepartors = { '\"', '[', ']', '{', '}', ':' };
private static char[] csvSperators = { ',', ';' };
public static char[] GetSeperators(ESperatorType p_speratorType)
{
if (ESperatorType.JSON == p_speratorType)
return jsonSepartors;
else if (ESperatorType.CSV == p_speratorType)
return csvSperators;
else
return null;
}
}
class Program
{
static void Main(string[] args)
{
foreach (char c in Seprators.GetSeperators(ESperatorType.JSON))
{
Console.Write(c);
}
Seprators.GetSeperators(ESperatorType.JSON)[0] = 'A';
Console.WriteLine();
Console.WriteLine("First element of jsonSepartors will now be 'A'");
foreach (char c in Seprators.GetSeperators(ESperatorType.JSON))
{
Console.Write(c);
}
Console.Read();
}
}
The answer is : No, you could see on the console that the first character changed to 'A'
Scenario1.1: Do you think a smart way will be make jsonSepartors read only?
private readonly static char[] jsonSepartors = { '\"', '[', ']', '{', '}', ':' };
The answer is : No the issue still remains the same.
Scenario1.2: What about this way:
(char[])jsonSepartors.Clone();
The answer is : Oh No, this is too dirty, this solution works but this has serious performance implications.
The solution is not about container type but about data type. For private and read-only data, consider not to use array for storage or if you are constrained to use array, you could simply restrict the Interface by using ICollection at the GetSeparators
Method.
Scenario1.3: Combo solution: Not using array and restrict the Interface
public enum ESperatorType { JSON, CSV }
public class Seprators
{
private static ICollection<char> jsonSepartors = new[] { '\"', '[', ']', '{', '}', ':' }.ToList().AsReadOnly();
private static ICollection<char> csvSperators = new[] { ',', ';' }.ToList().AsReadOnly();
public static ICollection<char> GetSeperators(ESperatorType p_speratorType)
{
if (ESperatorType.JSON == p_speratorType)
return (jsonSepartors);
else if (ESperatorType.CSV == p_speratorType)
return (csvSperators);
else
return null;
}
}
class Program
{
static void Main(string[] args)
{
foreach (char c in Seprators.GetSeperators(ESperatorType.JSON))
{
Console.Write(c);
}
Seprators.GetSeperators(ESperatorType.JSON).Add('A');
Console.WriteLine();
Console.WriteLine("First element of jsonSepartors will now be 'A'");
foreach (char c in Seprators.GetSeperators(ESperatorType.JSON))
{
Console.Write(c);
}
Console.Read();
}
}
For further information, please visit:
Collection
Before going through collections complexities, here is a brief reminder of typical complexities:
Complexity | Notation | Description |
---|---|---|
constant | O(1) | Constant number of operations, what ever input size is |
logarithmic | O(log n) | Number of operations is log-proportional to the input size |
linear | O(n) | Number of operations is proportional to the input size |
Quadratic | O(n²) | Number of operations is proportional to the cube of the input size |
Exponential | O(2^n) | Number of operations is exponentially proportional the input size |
Here is a summarized table of collections
Collection | Insertion | Access | Erase | Find | Notes |
---|---|---|---|---|---|
------------- | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
------------- | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
------------- | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
------------- | :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |