More technical Csharp
Article Author(s): Niels Van Vliet
All Rights Reserved.
Introduction
C# is a simple language, and so are all topics of this page. But when I learnt C#, I focused on several obscure parts of C#. I wrote some of them on this page, which was used as a giant post-it. This is why most paragraph are about C#, but not all of them. Some are about .Net, C++ or C. Example: in C, a function is not a data, and so a function pointer can not be casted into a data pointer. Also some question are still pending.
The aim of this page is not to teach C#, so, you are expected to understand the subjects before reading the paragraph on a given detail.
I wrote that a long time ago, whith a poor C# level (I use to program in C++), so some parts might be incorrect. If you find any error or lack of precision modify the page, or send me an email (niels AT nvv DOT name).
Memory access
Buffer.BlockCopy faster than Array.Copy
Buffer.BlockCopy is less secure than Array.Copy, but performs less operations. Example: in Array.Copy, if a lower index is provided, it is taken into account (the lower bound can be different from 0, which is often the case if it is allocated from a VB.Net of COM code) [[1]]. Also, cast constructors are called in a different way.
Note that Array.Copy and Buffer.BlockCopy use an algorithm like memmove, and not like memcpy. In C, memcpy can be faster than memmove, but might produce incorrect result if the intput and output arrays are overlapped. In theory, memcpy(c, c+10, 15), can produce a different array than the memmove, but using two compilers (Visual 5 sp1, and g++ FIXME), I could not produce this different behaviour.
BaseClass[] is different in C# and C++
There is no implicit cast in C++ from an array of pointers to instances of a BottomClass (BottomClass**) to an array of pointers to a base class of BottomClass**, even if BottomClass inherits form BaseClass:
class BaseClass {};
class BottomClass: public BaseClass {};
int main(){
BottomClass* p1;
BaseClass * b1 = p1; // Ok, of course.
BottomClass** p2;
// BaseClass ** b2 = p2; Does not compile: 'error: invalid conversion from `BottomClass**' to `BaseClass**';
}
We will see it is different in C#, but first I am going to show why I like this behaviour. Let's imagine that I write the following function:
typedef BaseClass * PtrBaseClass;
static void SetFirstElt(PtrBaseClass vecWithMoreThanZeroElt[], PtrBaseClass firstElt){
vecWithMoreThanZeroElt[0] = firstElt;
}
A quick look at this function gives you the impression that it is type safe. This is true in C++: the following code does not compile:
BottomClass* b[5]; // SetFirstElt(b, new BaseClass()); // does not compile: error: invalid conversion from `BottomClass**' to `BaseClass**'.
In C#, an implicit conversion form BottomClass[] to BaseClass[] exists. So the following code compiles, and throws during the execution.
class M{
class BaseClass {}
class BottomClass : BaseClass {}
static void SetFirstElt(BaseClass[] vecWithMoreThanZeroElt, BaseClass firstElt) {
vecWithMoreThanZeroElt[0] = firstElt;
}
static void Main()
{
BottomClass[] b = new BottomClass[5];
SetFirstElt(b, new BaseClass()); // It compiles, but 'vecWithMoreThanZeroElt[0] = firstElt' will throw.
}
}
My understanding is that this behaviour was usefull when there where no template (C# 1.0). Note also that a check has to be done in the line vecWithMoreThanZeroElt[0] = firstElt so has to check that the left hand type inherits (or is, or implemts) the right hand type. I tried to show that this can decrease the performance (in comparison with a function that uses BottomClass as argument), but I failed to find relevant difference.
Zero base array and ToString() on types
The following piece of code prints the result of ToString() on the Type returned by an instance of array:
static void Print(Array a) {
Console.WriteLine(a.GetType());
}
- If the array has 1 D:
- If the array is zero-based: it prints "YourObject[]".
- If the array is NOT zero-based: it prints "YourObject[*]".
- if the array is N D (with N >=2): it prints "YourObject[" followed by N-1 comma, followed by "]".
Interesting details:
- All multi dimensional arrays are NOT tagged as zero-based in the CLR, even if their lower bounds are 0.
- So, all multi dimensional arrays are not zero based, one could expect to print "YourObject[*,*,*]" for any 3D array of type YourObject. This is not the case, all are whithout stars, maybe for sake of simplicity.
I would like to emphasize that any array that has more than one dimension is not tagged as zero based, thus the arithmetic to access an element involves more instructions than one would think (example for a 2d array: the formula does not have the form lenght0 * idx1 + idx0, but length0 * (idx1 - zero1) + idx0 – zero0).
Print(Array.CreateInstance(typeof(string), // [4..5]
new Int32[] { 2 }, new Int32[] { 4 })); // System.String[*]
Print(Array.CreateInstance(typeof(string), // [ 7..8, 4..6, 1..2]
new Int32[] { 2, 2, 2 }, new Int32[] { 7, 4, 1 }));// System.String[,,]
Print(Array.CreateInstance(typeof(string), // System.String[0..2,0..2,0..2]
new Int32[] { 2, 2, 2 }, new Int32[] { 0, 0, 0 }));// System.String[,,]
// This is the only zero-based array of the group:
Print(Array.CreateInstance(typeof(string), // [0..1]
new Int32[] { 2 }, new Int32[] { 0 })); // System.String[]
Sizeof
sizeof is not as useful in C# as in C, because the memory is managed. Let's start to see how sizeof behave on char:
sizeof(char) == 2
sizeof returns the number of allocated bytes by the CLR, including padding. For the char, it uses two bytes (Unicode). In C++ character types are very complex (if you go into details), and there are several difference between C and C++. In C#, it is also very complex, but clearer.
Now, let's test the size of int:
sizeof(int) == 4;
Even on x64 proc, it is 4, because int is a shortcut to System.Int32 (and 32 / 8 = 4).
sizeof(decimal) == 16
No comments. Now, we are going to define a structure FooStruct:
struct FooStruct {
public int i;
public decimal d;
}
sizeof(FooStruct) does NOT compile. sizeof(Datetime) does NOT compile.
In fact, only a few number of types have a pre defined size (static). The size of the structure might depend of the padding of the class FooStruct. sizeof can not be called in unsafe environment on these objects.
Marshal.SystemDefaultCharSize == 2
Windows uses the Unicode system, thus on this system the default char size is 2. It might be different on another OS. But...
Marshal.SizeOf(typeof(char)) == 1
Marshal.SizeOf returns the number of byte after the marshalling to an unmanaged language. In this case, the marshalling changes the encoding and reduces the size of the char to 1 byte. This will ease, for example, the interface with the std::string, which are ANSI strings.
Marshal.SizeOf(typeof(FooStruct)) == 24 (and not 20!)
Note that Marshal.SizeOf(typeof(decimal)) == 16, and Marshal.SizeOf(typeof(int)) = 4. One reason might be padding (addition unused space to align data), but I do not understand why padding is necessary in this case. I tried to replace the decimal by a struct Bar with a size of 16 (ex: 4 int), but in this case Marshal.SizeOf(typeof(FooStruct)) == 20. If you know the answer, I would appreciate to receive it.
Let us continue:
Marshal.SizeOf(new DateTime()) throws an exception.
The reason is that the DateTime can not be marshalled.
unsafe { [...] sizeof(FooStruct) == 20 [...] }
20 = 16 + 4 + 0 * padding. No padding at this time in the allocated memory for the managed objects. Note that for the following structure:
struct BoolStruct {public int i1; public bool d2, d3, d4;}
unsafe { [...] sizeof(BoolStruct) == 8 [...] }
8 = sizeof(int) + 3 * sizof(bool) + 1 * padding. Note that we can call sizeof on BoolStruct and FooStruct only because they are unmanaged structure. A structure is unmanaged if all fields are unmanaged (see [Pointer types]. For example you can not use sizeof on:
struct FooStructManaged{public object o;}
And finally:
unsafe { [...] sizeof(DateTime) == 8 [...] }
Note that, in this example, it is the only result that succeeded for DateTime.
| Size in byte | Size in bits | Type |
|---|---|---|
| 1 | 8 | byte sbyte bool |
| 2 | 16 | short uhort char(Unicode) |
| 4 | 32 | int uint float |
| 8 | 64 | long ulong double |
Optimisation of the size of a struct
By default, the layout of a struct is Sequential:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] // <- this is the default
struct StructDefault
{
public bool b; // 1 byte (line to swap).
public int i; // 4 bytes (line to swap).
public bool o; // 1 byte.
}
The size of this structure in memory is 12 bytes.
If you swap the bool with the int according to the comments, the size of this structure will be only 8 bytes long. You can let the compiler optimize the order of the fields:
[StructLayout(LayoutKind.Auto)] // <- this let C# optimize the order of the fiels.
struct StructOptimized
{
public bool b; // C# might change the
public int i; // order of these
public bool o; // fields.
}
The size of this struct is now 8 bytes.
The LayoutKind.Auto is not the default because it produces not deterministic code if some code relies on the order of your structure. It can be the case if you send or receive a structure from unmanaged code, or if you use unsafe code that makes an assumption on the order of the fields.
You can use LayoutKind.Auto without worry if your structure has no interaction with unmanaged code and is not used in unsafe contexts.
Creating a C union in C#
This creates a 'C style' union of a double and a byte:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct MyUnion
{
[FieldOffset(0)]
public double Double;
[FieldOffset(0)]
public byte Byte;
}
Size of (MyUnion) == max(size of byte, size of double) = max(1, 8) = 8. If no layout is provided, the size is 12 (=8+1+ 3 padding).FIXME TO BE CHECKED.If you want to test it yourself, note that sizeof can only be called on non-generic types (and should be tagged unsafe).
There is several restriction with explicit layout:
- creation of a new instance throws an TypeLoadException exception if the class/struct is generic. Ex:
[StructLayout(LayoutKind.Explicit)] struct MyUnion0<T>{} // Compiles without warnings.
static int main{} {
MyUnion0<T> b; // <- throws a TypeLoadException.
return i.Equals(0) ? 0 : 0;}
- reference type can not be overlapped with value types. For instance, the following code will throw on 64 bits chips, but does not throw on 32:
[StructLayout(LayoutKind.Explicit)]
struct MyStruct
{
[FieldOffset(0)]
public object first;
[FieldOffset(sizeof(Int32) * 1)] // The second field is overlapped if the
public int second; // size of the reference of 'first' is > 32bits.
} // In this case the 'new' will throw
Null pointer check in C#, not in C++
This piece of C++ code calls a non static method on a null object:
#include <iostream>
struct S{
int Question() const { return 42;}
};
int main(){
std::cout << ((S*)(NULL))->Question() << std::endl; //(You should not do that.)
}
You can expect a segmentation fault (access of memory which does not belong to your process). But for several compilers this code prints 42, whithout exception, and whithout compile time warning. This is the case for g++ (FIXME version), and Visual 2005, with and without optimization. This is not a but of the compilers.
The C# equivalent must throw a NullReferenceException, unless there is a bug in the C# or JIT compilers.
Now, let's compare two methods:
struct S{
public int NonStatic() { return 42;}
public static int Static() { return 42;}
}
A call to NonStatic looks a like that:
IL_0068: ldloc.1 IL_0069: callvirt instance int32 M::NonStatic() // 'callvirt' checks if instance != null, IL_006e: pop // and then calls the method.
A call to 'Static looks like that:
IL_004a: call int32 M::Static() // 'call' just calls the method. IL_004f: pop
One could expect a difference of speed between the two versions. I have done simple tests (10 calls inside a 500000x50000 loop), and there is no significant difference of speed between the methods, and no significant difference between the same program written in C++ (using g++ -O3).
Conclusion: you have loose your time reading this section. :)
Const and readonly
Changing readonly fields using reflection
Note: of course, you should never do that.
Using reflection, it is possible to do unexpected things. The following piece of code compiles without warnings, and creates a weird behaviour.
using System;
using System.Reflection;
static class MyClass
{
public static readonly int modifyMe = 51;
public static int ModifyMe {get { return modifyMe; }}
public static void Change() {
typeof(MyClass).GetField("modifyMe").SetValue(null, 1664);
}
#if WITH_CTOR
static MyClass() { }
#endif
}
static class M2{
static void Main(){
Console.WriteLine(MyClass.modifyMe); // Dislay n°1.
MyClass.Change();
Console.WriteLine(MyClass.ModifyMe); // Dislay n°2.
Console.WriteLine(MyClass.modifyMe); // Dislay n°3.
}
}
If WITH_CTOR is defined:
- 51.
- 1664. The readonly field has been changed!
- 1664. This seems obvious, but read the next test...
If WITH_CTOR is NOT definied:
- 51.
- 1664.
- 51!
The only difference in the MSIL is the modifiers of MyClass:
WITH_CTOR : .class private abstract auto ansi sealed MyClass extends [mscorlib]System.Object NOT WITH_CTOR: .class private abstract auto ansi sealed beforefieldinit MyClass extends [mscorlib]System.Object
According to MS help beforefieldinit specifies that calling static methods of the type does not force the system to initialize the type.
I guess that when Main is compiled on the fly, the field is inlined because it is static readonly. FIXME: check in the standard if it is a MS bug or not.
Changing readonly fields using unsafe code
Note: of course, you should never do that.
The previous section explains how to change a readonly field using reflexion. It might be easier to use unsafe code, don't you think? The goal is to write the method ModifyConst:
class M
{
[StructLayout(LayoutKind.Sequential)]
struct Const
{
public Const(int i, int j) {this.i = i; this.j = j;}
public int i;
public readonly int j;
}
static void Main()
{
Const c = new Const(16, 64);
ModifyConst(ref c);
Console.WriteLine(c.j); //Print 51.
}
static void ModifyConst(ref Const c)
{
[...]
}
}
My first try was not successful:
unsafe{fixed (int* pj = &c.j){ *pj = 51; }} // Does not compile.
It is not possible to take directly the address of a readonly variable. But it works if you access to the field indirectly:
unsafe{fixed (int* pi = &c.i){
int* pj = pi + 1;
*pj = 51;
}
This code also works with classes. Only change struct Const into class Const.
Const and enum are statically linked
This section is important, and is NOT a detail of C#. Constants and enums can be a source of bugs if you do not understand what means statically linked. We are going to create a class with one constant and one readonly variable assigned to the value of the constant. A call to the two variables will return two different values!
Create a.cs:
public class ClassA {
public const int f_cst = 16;
public static readonly int f = f_cst;
}
Compile it:
csc.exe /out:a.dll /t:library /o+ a.cs
Create b.cs:
using System;
public class M {
public static void Main() {
Console.WriteLine(ClassA.f_cst * 100 + 64);
Console.WriteLine(ClassA.f * 100 + 64);
}
}
Compile it and run it:
csc.exe /out:b.exe /t:exe /o+ b.cs /r:a.dll b.exe
This displays:
1664 1664
Now change a.cs:
public class ClassA {
public const int f_cst = 100; // 16 has been changed into 100.
public static readonly int f = f_cst;
}
Then compile a.dll, but NOT b.exe, and run b.exe. It displays:
1664 // <- this is not expected. 10064
Conclusion:
- public constants and enums should not change through time. Example of stupid piece of code: public const string version="1.1.0.0".
- If you need a public access to a constant, add a public method (in the same assembly).
- enum and const values are inlined (good for performances).
Technical note: if the visibility is internal, you are not out of trouble, particularly if you use "friend assemblies" (see #Friend assembly).
out and ref
Incompatibility of ref AND out overloads
Let's start with the following example:
class M
{
public static string Concat(string s1, char separator, string s2)
{
return s1 + separator + s2;
}
public static string Concat(string s1, out char separator, string s2)
{
separator = ';';
return Concat(s1, separator, s2);
}
//L 1: public static string Concat(string s1, ref char separator, string s2) { return "Does NOT compile"; }
static void Main()
{
char separator;
string s = M.Concat(M.Concat("This", out separator, "is"), separator,
M.Concat("an", separator, "example")); //L 3
Console.WriteLine(s);
}
}
Two remarks:
- The two functions differ only by out. But if you uncomment the line L 1, it does NOT compile. In fact you can not have two functions, which differ only by out and ref. But you can have nothing and out and nothing and ref.
- Line L 3, the second separator is not prefixed by out, and there is no warning or error. Contrary to C++, the order of call of a function is precisely defined in C#: from left to right. In C++, because the compiler can emit code that evaluates first the right argument of a function, this code could be incorrect (i.e. the separator could be not initialized line L 3).
out and ref support parametric polymorphism but not ad-hoc polymorphism
Let's say that we want to write a function to copy a reference to another, and return true if the reference is not null. The following piece of code seems generic, but is not:
class M
{
static bool Copy(object input, out object output)
{
output = input;
return input != null;
}
static void Main()
{
string s;
Console.WriteLine(M.Copy("Hello", out s)); //Does not compile.
}
}
The problem is that ref and out require an exact match. Even if "string is object" equals true, the above piece of code does NOT compile. In fact we could try to transform the call:
{
string s; //Initialization to null is required to compile, but
object o = s = null; // not neccessary for the execution.
Console.WriteLine(M.Copy("Hello", out o));
}
But it is not nice: the string s has to be initialized so as to be casted to an object.
A nice solution is the use of templates:
class M
{
static bool Copy<T>(T input, out T output)
{
output = input;
return input != null;
}
static void Main()
{
string s;
Console.WriteLine(M.Copy("Hello", out s)); //This works. Note that the template
} // is deduced (Copy<string>).
}
Allocation of elements of an array on the stack
Introduction
There are several advantage and disadvantages to allocate on the heap. The allocation, deallocation, access of memory is usually faster on the stack. The heap is usually more flexible; in term of size, choice of the size of the array at run time, and passing the array to other pieces of code.
The C++/C#/JIT compilers can use a different allocation procedure that what is expected in some special cases. In C++ it is rare, in C#/MSIL it is common. Also note that the allocation on the heap is, in most case, much faster in C# than in C++.
The two next sections explain how to allocate elements of an array on the stack.
Stack alloc using public fixed T []name=new T[xxx]
In C++, it is trivial to allocated the elements of an array on the heap or on the stack:
// C++ Stack.
// Size on stack: (1+5)*sizeof(int)
// Size on heap : 0
struct fooBarStack {
int foo;
int bar[5];
};
// C++ Heap
// Size on stack: 1*sizeof(int) + sizeof(int*)
// Size on heap : 5*sizeof(int)
struct fooBarHeap {
fooBarHeap():bar(new int[5]) {} //Note: it should be deleted somewhere.
int foo;
int *bar;
};
In C#, by default it is allocated on the heap:
//C#:
struct fooBarHeap
{
public int foo;
public int[] bar = new bar[5];
}
The pointer new fooBarHeap().bar is allocated on the stack, but the elements of the array are on the heap. public fixed Int32 can be used to allocate the elements on the stack.
// C#:
unsafe struct fooBarStack{
public int foo;
public fixed Int32 bar[5];
}
You can read and write in new fooBarHeap().bar[0] only in unsafe mode.
Stack alloc using stackalloc
In C and C++, it is possible to use [alloca] to allocate on the stack.
In C#, the keyword stackalloc is equivalent. Here is a short example:
unsafe{
int i;
int *d = stackalloc int[5];
d[1] = 3;
}
You can use stackalloc only on value type. FIXME: explain what is value type (pretty complex) and buffer overrun.
Order of evaluation
Let us start with this example:
int[] tab = { 0, 1, 1 };
tab[tab[0]++] = tab[0]++;
The result is:
tab[0] == 1 && tab[1] == 1 && tab[2] == 1
FIXME: explain.
Class and struct
Casting a struct build a new object
The following piece of code does NOT change the structure, because casting a structure which is not boxed, result in a new structure:
interface IChange{
void Doit();
}
struct Foo : IChange
{
public Foo(int j) { i = j; }
public int i;
public void Doit() { i = 5;}
static void Main() {
Foo f = new Foo(3);
((IChange)f).Doit(); // Call Doit on a temporary copy!
Console.WriteLine(f.i); // Print 3 (unchanged!)
}
}
Now, let's see a related problem. Imagine that you have to store your structure in a Dictionary<string, object>. For all values of this dictionary, you need to call 'Doit()' on all Foo structures:
static void Apply(Dictionary<string, object> dico)
{
foreach(object o in dico.Values)
if (o is Foo)
{
((Foo)o).Doit(); // Does NOT modify the dictionnay (DoIt is called on a temporay copy).
((IChange)o).Doit(); // Modify the dictionnary.
}
}
Conclusion:
- In order to avoid confusion, all structs should be immutable.
- If you really need a mutable method on a struct, this method should implement an interface. Otherwise, it is almost not possible to modify a boxed structure (it is possible using, for example, unsafe code).
Constructor
Chain of constructors possible in C# and not in C++
It is possible to chain constructors in C#:
class Foo{
public Foo(string s, int j) {
}
public Foo(string s): this(s, s.Length * 2){
}
}
Now we are going to compare the speed of different initializations. The performance is just an indication, and may vary a lot (done on my computer using a 90000000 loop with a new Bar).
Chain of constructors add a method call, but can also help to keep your code small and efficient.
Here are 3 implementation of the class Bar, with different performances.
class Bar1
{
double c1 = Math.Exp(1);
double c2 = Math.Exp(2);
double c3 = Math.Exp(3);
public Bar1()
{}
// 30% slower than Bar1() and Bar2(double).
public Bar1(double c_given) // Slow: Math.Exp(3) is called,
{ // and is then overridden.
c3 = c_given;
}
}
class Bar2
{
double c1, c2, c3;
public Bar2()
{
c1 = Math.Exp(1);
c2 = Math.Exp(2);
c3 = Math.Exp(3);
}
public Bar2(double c_given)
{
c1 = Math.Exp(1); // Efficient but bad coding style:
c2 = Math.Exp(2); // it will be hard to maintain it.
c3 = c_given;
}
}
// clean
class Bar3
{
double c1, c2, c3;
// 10% slower than Bar1() and Bar2(), but it is clean.
public Bar3():this(Math.Exp(3))
{ }
public Bar3(double c_given)
{
c1 = Math.Exp(1);
c2 = Math.Exp(2);
c3 = c_given;
}
}
I guess the last one is 10% slower because of the function call.
Call order of constructors is different in C# and C++
In C++, constructors are called starting from the base class. Thus, a virtual call from the base class calls the function implemented in the base class (if implemented).
In C#, the derived class is first constructed, and then the base class. So, a virtual call from the base class, calls the overriden implementation. This explains why the creation of 'b' in the code below uses the derivated version of Print.
But, the call order seems to be the same than in C++: the code inside the base constructor is executed first. In the example below, when 'b' is constructed, "Top Ctor" is printed before "Bottom Ctor". The reason is that a call to the base class is added automatically at the beginning of each constructor, in this example just before Console.WriteLine("Bottom Ctor: " + Print());.
Note: calling a virtual function a constructor should be avoided, because it is error prone. In this case, fields of the derived class are not initialized (or, more exactly, initialized to 0 or null) so the overriden function might have a behaviour not expected.
class Top {
public Top() {
Console.WriteLine("Top Ctor: " + Print());
}
protected virtual string Print() {
return "Top";
}
}
class Bottom:Top{
public Bottom(){
Console.WriteLine("Bottom Ctor: " + Print());
}
protected override string Print()
{
return "Bottom";
}
}
class Test {
static void Main() {
Top t = new Top(); // Display "Top Ctor: Top"
Top b = new Bottom(); // Display "Top Ctor: Bottom" and then "Bottom Ctor:Bottom"
}
}
A section in this page explains the call order of static method.
When a class is called, first all initializers are called from the most derived class to the base class. Then all the bodies of the constructor are called from the base class to the most derived class (so the opposite order):
using System;
class Member{
public Member(string s)
{Console.WriteLine("Member:\t\t" + s);}
}
class Base{
readonly Member m = new Member("Base");
public Base(){Console.WriteLine("Constructor:\tBase");}
}
class Middle:Base{
readonly Member m = new Member("Middle");
public Middle() {Console.WriteLine("Constructor:\tMiddle");}
}
class Derived : Middle
{
readonly Member derivedMember = new Member("Derived");
public Derived() {Console.WriteLine("Constructor:\tDerived");}
}
static class Program{
static void Main()
{new Derived();}
}
This program displays:
Member: Derived Member: Middle Member: Base Constructor: Base Constructor: Middle Constructor: Derived
Throw in static constructor
Let us write a piece of code with a throw statement in a static constructor. Then we are going to explain the results.
using System;
struct S
{
public int non_static;
public static int stat;
static S()
{
stat = 5;
Console.WriteLine("I am going to throw.");
throw new Exception("My message.");
}
}
static class M2
{
static void Main()
{
{// Part 1. Nothing throws!
S s;
s.non_static = 1;
Console.WriteLine(s.non_static);
}
{// Part 2. Now, it throws.
try
{
S.stat = 2;
}
catch (Exception e1) // Print: 'I am going to throw.'
{
// Print: 'Message: The type initializer for 'S' threw an exception.'
Console.WriteLine("Message: " + e1.Message);
// Print: 'Inner message: My message.'
Console.WriteLine("Inner message: " + e1.InnerException.Message);
}
}
// Part 3. It throws, but the static ctor is NOT called.
{
try
{
Console.WriteLine(S.stat);
}
catch (Exception e2) // Does ** NOT ** print: 'I am going to throw'
{
// Print: 'Message: The type initializer for 'S' threw an exception.'
Console.WriteLine("Message: " + e2.Message);
// Print: Inner message: My message.
Console.WriteLine("Inner message: " + e2.InnerException.Message);
}
}
}
}
Part 1: Static Ctor of struct not called immediately.
As you can notice the first part does not throw, even if there is a throw in the static constructor of S, and that we use S. The static constructor of a struct is NOT called before the first access to the struct, but before a static field or member is used. If you want to be sure that a static constructor is called, you can use RunClassConstructor:
// Call the static constructor (if it has not been done before). System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(S).TypeHandle);
If the struct is replaced by a class and S s; is replaced by S s = new S();, this would throw. Because the static ctor of a class is called before any access to a static field or method AND before the construction of an instance of this class.
Section #call order static methods provides an exception to these rules.
Part 2: It throws.
This is what one would expect.
Part 3: It re-throws.
Of course, it throws again. But what you can notice is that I am going to throw is not displayed this time. More precisely, the constructor is just called once, and then the same exception is thrown. Thus e1 == e2.
Conclusion
One attempt, and only one, is done to call the static constructor of a struct. This attempt is not done before the first use of the struct, but before the first use of a static field or method of the struct.
call order static methods
Let us work with these two classes:
class Base
{
static Base()
{
// 1. S.FooS();
Console.WriteLine("static Base().");
}
static public void FooBase(){}
}
class S:Base
{
static public void FooS() {}
static S()
{
//2. Base.FooBase();
Console.WriteLine("static S().");
}
}
Now let's create a new S:
S s = new S();
If 1 and 2 are hidden, the result will be:
static S(). static Base().
By the way, note that the two instance constructors are called AFTER the two static constructors.
Now, if line #2 is commented; if call Base.FooBase(); in static S(), the compiler change the order, according to the dependency:
static Base(). static S().
Now, if you also call S.FooS() in static Base(), there is no 'correct' order. Here is what is printed:
static Base(). static S().
Note that a static function of S is called BEFORE the initialization of S. Thus you should avoid cross calls inside static constructors.
Conclusion: a method can be called before the static constructor in C#.
Static constructor in a multi-threaded context
In a multi threaded context, there is no particular problem. A lock is used in the static constructor. If several threads call the same static constructor, the first one will lock it at call it. Others will wait until the lock is released.
Here is an example:
using System;
using System.Runtime;
using System.Threading;
class MyClass
{
public int Quick() { return 1; }
static MyClass()
{
Console.WriteLine("Going to initialize..."); // Printed only ONE time.
Thread.Sleep(5 * 1000);
Console.WriteLine("done.");
}
}
static class M2
{
static void Print()
{
Console.WriteLine(new MyClass().Quick());
}
static void Main()
{
Thread t1 = new Thread(Print);
Thread t2 = new Thread(Print);
t1.Start();
t2.Start(); // The second thread will also have to wait.
}
}
Precise static constructor can be slow
Two methods (F1 and F2) read a static readonly int. One function is going to be up to 20 times slower!
using System;
using System.Diagnostics;
static class MyClass
{
public static readonly int i = 51;
#if WITH_CTOR
static MyClass() {}
#endif
}
static class M2
{
static void F1()
{
int j = MyClass.i;
}
static void F2()
{
int j = MyClass.i;
}
static void Main()
{
#if WITH_RUN
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
typeof(MyClass).TypeHandle);
#endif
Stopwatch sw = Stopwatch.StartNew();
int n = 10000000000;
for (int i = 0; i < n; ++i)
F1();
Console.WriteLine("D1 : {0}." , sw.Elapsed);
sw = Stopwatch.StartNew();
for (int i = 0; i < n; ++i)
F1();
Console.WriteLine("D2 : {0}.", sw.Elapsed);
sw = Stopwatch.StartNew();
for (int i = 0; i < n; ++i)
F2();
Console.WriteLine("D3 : {0}.", sw.Elapsed);
}
}
- If the ctor is NOT defined, the static class is tagged with BeforeFieldInit in the MSIL. The JIT is then allowed to initialize the class a 'long' time before the first access to it. So the JIT compiler can optimize the code and does not check within the loop if the class has or has not been initialized.
- If the ctor is defined, the class is NOT tagged with BeforeFieldInit. The JIT has to call precisely the static constructor just before the field is initialized. Thus, when F1 is JIT compiled, the class MyClass is encountered for the first time. In the case of MyClass, the JIT compiler could call the static constructor, and then produces the function F1. But in more complex scenarios, it would be different. Let's see what happen in the following example: the body of F1 is if (false) j = MyClass.i and that the static constructor has a side effect. Even if MyClass is part of the function, because the static class is not marked BeforeFieldInit, it has to call the constructor just before the class is used (i.e. just after the if, and not during the JIT compilation of the function F1). Thus, the JIT compiler will produce the code of F1, and in the code of F1, there will be a check ('is MyClass initialized?'). Of course if this check returns false, the class will be initialized. But what interests us is this check. The point is: it slows down the function F1. So each time you will call F1, a test will be ran (even if it will be true only one time).The function F2 will 2 times more efficient. The reason is that when the code of F2 will be emitted, the JIT compiler has already call the constructor, and thus test can be ignored in F2. But what is odd is that the function F2 is (still) 10 times slower than the case 'with static constructor'.
| X | With static Ctor | withOUT static Ctor |
|---|---|---|
| With run | D1:43 D3:43 | D1: 4 D3: 4 |
| WithOUT run | D1:86 D3:43 | D1: 4 D3: 4 |
Destructor is virtual
| X | ~MyClass | virtual ~MyClass |
|---|---|---|
| C# | a *virtual* function | <<Error>> |
| c++ | a final constructor | a virtual constructor |
Accessibility
Different accessibility in set and get
It is possible to reduce the visibility of a set or a get:
protected int Foo{
get { return foo; }
private set { foo = value; }
}
Here are the rules to apply it:
- It is possible to reduce the visibility of only one of them (get or set, but not both).
- The restriction can only be stronger. If the property is [X] => these accessibilities can be applied:
- private => no other restriction can be applied.
- protected => private.
- internal => private.
- protected internal => protected or private.
- public => protected or protected internal or private or internal.
Internal
protected internal
- protected: callable by derived types.
- internal: callable only by the current assembly (or a #Friend assembly).
- protected internal: callable from a type which is in the same assembly OR a derived type (or from a #Friend assembly).
I tried to write a design pattern to have a method F(int arg1, int arg2) callable only from a type which is derived AND the same assembly. In fact I failed to write one that is checked statically. Go head if you can improve it.
// Assembly One.
using System;
public sealed class Internal
{ internal Internal() { } }
public class Base {
protected void F(Internal i, int arg1, int arg2)
{
// I am not happy about that:
if (i == null)
throw new NullReferenceException("Internal i == null");
[...];
}
}
class C : Base {
public void G()
{ F(new Internal(), 4, 5); }
}
// Assembly Two.
class B : Base
{
protected void H()
{
F(new Internal(), 4, 5); //Does not compile: Cool.
F(0, 4, 5);; // Compile. NOT cool. (I had to add the (i == null).
}
}
internal class
By default class are internal. Thus the two following types have the same properties:
namespace Foo{
class A(){}
internal class B(){}
}
You can change the default behaviour by adding public:
namespace Foo{
public class C(){}
}
- internal class B: the class is visible only from this assembly (and #Friend assembly).
- public class C: the class is visible by any assembly that reference the assembly that contains C.
Note: 'private', 'protected', 'protected internal' can NOT be applied.
Friend assembly
If you develop an assembly A for a third party, most classes will be internal, and only a few of them will be public. Imagine that you develop another product APlus, and that you wish to access to the internal classes of A. This is possible using the InternalsVisibleTo attribute. In the assembly A you can have:
[assembly:InternalsVisibleTo("APlus")]
- If APlus is built using a module b.NetModule, you can specify at compile time that b.NetModule is going to be included in the module APlus (thus can access the fields of A, because A allows APlus to use internal classes): /moduleassemblyname:APlus
- If APlus is strong named, you can specify the strong key (change "APlus" into "APlus, PublicKey=a1111b11 .... 890f" in the above example).
FIXME: explain what is a link demand.
Access to internal, protected and private members, using reflexion
It is possible to call a function which is hidden (private, protected, or internal), using reflexion.
f.GetType().InvokeMember("HiddenFunction",
BindingFlags.InvokeMethod| BindingFlags.NonPublic | BindingFlags.Instance,
null, f, args);
Accessibility of an event
public event are not exactly public
If an event is declared as public, it means that the Add and Remove operators generated by the C# compiler are public. But the delegate generated by C# will be private:
class LogBase
{
public event EventHandler<NewMessageEventArgs> NewMessage;
public void ToNullBase() { NewMessage = null; } // Ok, can access to the private generated delegate.
}
class LogInherited:LogBase
{
// public void ToNullInHerited() { NewMessage = null; } Does NOT compile, only += or -= works.
}
It has the same behaviour with protected, protected internal or internal. It has also (with some change) the same behaviour if the event is static. If the add and remove methods are written, the delegate is not generated, thus the meaning of public is exactely public.
If the modifier 'virtual' is used... FIXME: to be completed. (should explain that even if the event is virtual, two different delegates are declared).
Virtual event
If a virtual event is overridden, and that the override does not define explicetly the add and remove methods, the C# generated another delegate in the inherited class. In the example above, an instance of LogInHerited_two holds two delegates. Both are generated by the C#. The first one is accessible from the base class only (LogBase), the second one from LogInherited_two. At line L15, the delegate of the base class is NOT modified; NewMessage refers to the one generated in LogInherited_two:
class LogBase
{
public virtual event EventHandler<NewMessageEventArgs> NewMessage;
public void ToNullBase() { NewMessage = null; } // Ok, can access to the private generated delegate.
}
/* Does not compile.
class LogInherited_one:LogBase
{
public virtual void ToNullInHerited() { NewMessage = null; } Does NOT compile, only += or -= works.
}
*/
class LogInherited_two:LogBase
{
public override event EventHandler<NewMessageEventArgs> NewMessage;
// Line L15: Read the note above. This does NOT modify the delegate of the base class.
public virtual void ToNullInHerited() { NewMessage = null; }
}
Note also that any modification (+=...) on the inherited class will NOT modify the base class, and a call to NewMessage(...) in the inherited class will be different than the 'same' call in the base class. Keep in mind that if you do not write explicitely the add and remove methods, C# will generate a new delegates for you (even if the delegate is virtual).
Conclusion: I think that an override event should always define explicitely the add and remove method.
abstract / sealed override
abstract override
The combinaison of the keywords abstract and override can be used to hide the implementation of a base class, and force its implementation in all inherited classes (see [the blog of Mike Atall]:
abstract class A
{
public virtual void Foo()
{
Console.WriteLine("Hello");
}
}
abstract class B:A
{
// Discard the implementation of A.
public abstract override void Foo();
}
class C:B
{
// C HAS TO implement Foo, if not it does not compile, even if there is a definition in the class A.
public override void Foo()
{
Console.WriteLine("World");
}
}
FIXME: I failed to do the following thing:
- the goal is to write a function in the class C that calls the Foo implementation of the class A.
- without modifying A, and without modifying B.
- without adding any class that inherit directly form A.
- using ugly things if it is needed (reflexion, IL...).
sealed override
On the contrary to abstract override, you can use sealed override to forbid the derived class to override the method.
sealed override might also improve the performance, by adding a candidate to inlining.
abstract virtual (not is C#...)
abstract virtual would be very usefull, especially to implement methods that return a copy of an instance (ex: Clone, Shift...). In this cases, a new implementation is required in all classes. If an implentation is done in a base class, but not in the inherited class, the inherited class will build an object of a wrong type (base type, and not same type).
abstract virtual would mean: this has an implementation (virtual), but any derivated classes should re-implement it (abstract).
Or course it does not compile. Because abstract does not mean must be implemented in derived classes, but means must be implemented in derived classes, and not implementated in this class; the second part of the meaning is not compatible with virtual.
In most case it is possible to use the following pattern. But:
- The check is done at run time (not at compile time). => more errors, slower.
- You can not call the base implementation (using Base.Clone() in this case).
public class Bar : ICloneable {
// Check that the run time type of _this is exactly of type T.
public static void CheckType<T>(T _this)
{
if (_this.GetType() != typeof(T))
throw new InvalidCastException("A method of the class " + typeof(T).FullName
+ " is not overriden in the class " + _this.GetType().FullName + ".");
}
//public abstract virtual // <- This is no C#. Snif.
public virtual object Clone() { CheckType<Bar>(this); return new Bar(); }
}
public class Foo : Bar {
public override object Clone() { CheckType<Foo>(this); return new Foo(); }
}
public sealed class FooD : Bar {
public sealed override object Clone() { return new FooD(); }
}
new and interfaces
The mechanisme to find the method called is well known for the classes, but a little bit difference for the interfaces. Here is the complete explaination:
Easy: derived classes
Let us define a class B and a class D, an instance of D is a B.
Object ^ A void Print(); ^ B new virtual void Print(); ^ C ^ D override void Print(); ^ E ^ F new virtual void Print(); ^ G new void Print(); ^ H override void Print();
We are going to analyse the following case:
C i = new G(); i.Print();
- the static type of i is 'C'. So we are going to start with our finger on 'C'.
- if the static type contains the method, jump to the next point. Overwise, move your finger to the next base class, and repeate this stage. Note: if you are above Object, it means that your code does not compile.
- in the above example, at this point, your finger should be on B. This class contains the correct static declaration of Print.
- if the class where your finger is is the instance class (G) move to the next point. If the derivated class contains a new definition of Print, move to the next point. Else move your finger to the derivated class, and repeate this stage.
- In the above example, your finger should be on 'E'.
- If this class (E) defines Print, then it is this version which is used. Else, move your finger to the base class, and repeate this stage.
Thus the version used is the one defined in D.
Interfaces
Let's see the results of this test:
interface IPrint
{ void Print(); }
class A: IPrint
{ public void Print() { Console.WriteLine(".A" ); } }
class B: A
{ }
class C : B, IPrint
{ public new void Print() { Console.WriteLine(".C"); } }
class D :C
{ public new void Print() { Console.WriteLine(".D" ); } }
class E: C, IPrint
{ }
class M {
static void Main()
{
((IPrint)new A()).Print(); // ".A"
((IPrint)new B()).Print(); // ".A"
((IPrint)new C()).Print(); // ".C"
((IPrint)new D()).Print(); // ".C"
((IPrint)new E()).Print(); // ".C"
}
}
FIXME explain.
Generic
Constraints
It is possible to specify a constraint on parameter type (let us call this type T). This can be:
- 'Iinterface1[, Iinterface2,...]. In this case any method of these interfaces can be called on a T.
- ClassB[,Iinterface1,...] . In this case T should inherit from this class. Note that it is possible to have both a class and interfaces (in the case the class does not implement some interfaces, the T provided should be a derived class of this class, which implement all interfaces). Note also that the class T can not be sealed.
- struct: specify that it is a value type. It can be an enum, a primitive (int), or a struct, which is not a Nullable<T>.
- class: specify that it is a referance type. It can be class, an interface, an array (including an array of value type), a delegate.
- new(). Note if it has the struct constraint, it implicitely has the new() constraint.
Constraint that are not possible:
- operator constaint, because it is static methods.
- static methods constraint. Snif.
- constructor constraint other than the default constructor. Note: a factory can be used.
- special class constraints:
- object. (Alternative: do not specify any constraint).
- Array. (Alternative: T[]).
- Delegate and MulticastDelegate. (Alternative: FIXME).
- ValueType. (Alternative: where T:struct).
- Enum. (Alternative: use reflexion).
- Void. (Alternative: do not use T).
- unmanaged struct (i.e. struct which contains only unmanaged type. For example the struct constraint is not restrictive enough to allow you sizeof or pointers.). (Alternative: generate code). (see [Pointer types] for more information on what is an unmanaged struct.
- no constraint.
There is several ways to overcom these problems. Some involve switch on the type and hugly casting, other that involve on-the-fly code generation.
Open types
It is possible to write some open type classes: templated classes WITHOUT providing the parameters.
using System.Collections.Generic;
class M {
static void Main()
{
Console.WriteLine(typeof(Dictionary<,>)); //System.Collections.Generic.Dictionary`2[TKey,TValue]
Console.WriteLine(typeof(Dictionary<int,double>));//System.Collections.Generic.Dictionary`2[System.Int32,System.Double]
//Console.WriteLine(typeof(Dictionary<int,>}); this line does NOT compile.
}
Template fake specialization
Using non template specializations
Using a non-template specialization of a templated function, it is possible to optimize some code. In the following code, the types DeriveOne and DeriveTwo are alike. A specialization of Process is given only for DeriveTwo. Because this class is sealed, the function F can be inlined. It is not the case for the template specialization for DeriveOne, because only one code is emited (so no inline possible). The call to Process using an object of type DeriveTwo is two time faster than a call to Process using an object of type DeriveOne:
class Base {
public virtual int F(int i) { return i * 65; }
}
sealed class DeriveOne:Base {
public override int F(int i) { return i * 66; }
}
sealed class DeriveTwo : Base {
public override int F(int i) { return i * 66; }
}
class M
{
public static int Process<T>(T t) where T : Base
{
return t.F(5); // Even if T is a sealed type, the virtual mechanism will be used. No inlining.
}
public static int Process(DeriveTwo t)
{
return t.F(5); // Because DeriveTwo is sealed, even if F is virtual it can be inlined.
}
static void Main()
{
int n = 1000000000;
Base b = new Base();
DeriveOne one = new DeriveOne();
DeriveTwo two = new DeriveTwo();
for (int i = 0; i < n; ++i) Process(b); // 8.57 s.
for (int i = 0; i < n; ++i) Process(one); // 8.57 s. (Even if one is sealed...)
for (int i = 0; i < n; ++i) Process(two); // 4.36 s. Youpi !
}
}
Different implementation for value type
A comparison of a value type with null emits a warning, if the value type is closed:
if (5 == null) {} // warning CS0162: Unreachable code detected
If it the object has a generic type (not constraint to a struct and and a class!) is does not emit warning:
static void Fun<T>(T obj){
if (obj == null)
{
// code if obj is a referance type.
} else
{
// code if obj is a value type.
}
}
If T is a class, the check will be performed, but the second part will NOT be JIT compiled. If T is a value type, the test will NOT be performed, and the first part will NOT be JIT compiled. So for a value type it will be equivalent to:
static void Fun<T>(T obj){
// code if obj is a value type.
}
Note: to check if a variable has been initialized to its default value do no use obj == null but:
if (obj == default(T))
Delegate
Covariance and contra-variance
Let's describe our example with the following classes and structures:
interface I {}
class A : object, I {}
class B : A {}
struct C {}
struct CI: I {}
And the following delegate:
delegate A Fun(B b);
The covariance of classes is allowed; so it is possible to use any method that return a class type which is or is derived from the delegate return type:
static B WithCovariance(B b) {return null;} // Return B and not A !
static void Main() {
Fun f = WithCovariance; } // Ok.
The contra-variance of classes is also allowed. So it is possible to use a method with arguments that are exactly a given type, or a basis of this class, or an interface implemented by this class:
static A WithContraVariance1(A a) { return null;} // Argument is A ans not B !
static A WithContraVariance2(I a) { return null;} // Argument is I ans not B !
The covariance and Contra-variance is not possible with value types. For example, even if (int is object), a method that returns a int can not be used if the delegate returns an object.
GetInvocationList
The result of a call to a multi cast delegate returns the result of the last call.
class M{
delegate int Fun();
static int F16() { return 16;}
static int F64() { return 64;}
static void Main()
{
Fun f = null;
f += F16;
f += F64;
Console.WriteLine(f()); // Call F16 and F64, but only the result of F64 can be used. Print:"16\n"
//GetInvocationList can be used to have a more control over the call to the multi cast delegate:
foreach (Fun d in f.GetInvocationList())
Console.WriteLine(d()); // Call F16 and F64, the two results are used. Print:"16\n64\n"
}
Using GetInvocationList you can do a lot of thing including:
- Changing the order of the calls.
- Use a pool of thread.
- Retrieve all results (see the above example).
- try-catch the calls (else it stops at the first throw.).
- Do not make all calls (ex: if there are more than 10 calls, perform only the delegates of types MyCriticalDelegate).
Local variables are copied if changed
Local variables, even if they are value type, are copied inside the delegate functor each time they are modified (more exactly, it has the same behaviour than that).
using System;
using System.Collections.Generic;
class Program{
delegate double f();
static void Main(string[] args)
{
List<f> lst_outside = new List<f>();
List<f> lst_inside = new List<f>();
double outside;
for (int i = 0; i < 3; ++i)
{
double inside = i;
lst_inside.Add(delegate { return inside; });
outside = inside;
lst_outside.Add(delegate { return outside; });
}
for (int i = 0; i < 3; ++i)
Console.WriteLine("inside[{0}]={1}\toutside[{0}]={2}",
i, lst_inside[i](), lst_outside[i]());
// output:
// inside[0]=0 outside[0]=2 (!)
// inside[1]=1 outside[1]=2 (!)
// inside[2]=2 outside[2]=2
}
}
Delegates are objects, void(*)(void) is not a void*
A delegate is a reference type which inherits from object. So in C#, you can cast a delegate into object, and vice versa.
On contrary, in C++, you can not cast a pointer of function into a pointer of object (void *) (and vice versa). This is true for any cast (including reinterpret_cast), and true for the C99. All good C++ compliers will emit a message of the form:
invalid conversion from `void (*)()' to `void*'
Note that Visual 2005 compiles by default with extensions to C++; you have to disable these Microsoft extensions in order to see the error message.
There are no standard way to cast a function pointer into a void *, the reason is that the size of function pointers can be different from the size of object pointers. Most compilers have an extension to enable this cast. BTW a pointer of function in C++ does not necessary hold the address of the function. For example, I read somewhere that on Linux 64 bits, with some version of g++, the pointer of function contains different information (address + some data. In this particular case, the size of this sum was equal the size of a pointer of object on this archi).
malloc() returns non-NULL does no guarantee that the memory is available
In C, you expect that if malloc() return not null, the memory is available, right?
Well here is a part of the man malloc:
By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. This is a really bad bug. In case it turns out that the system is out of memory, one or more processes will be killed by the infamous OOM killer. In case Linux is employed under circumstances where it would be less desirable to suddenly lose some randomly picked processes, and moreover the kernel version is sufficiently recent, one can switch off this overcommitting behavior using a command like # echo 2 > /proc/sys/vm/overcommit_memory See also the kernel Documentation directory, files vm/overcommit-accounting and sysctl/vm.txt.
Sorry, this has nothing to do in this page, but it is fun (at least for a geek).
C# behaviour
Equal
Different behaviours
Three behaviours:
- Equal and == compares the address: Ex: list<int>
- Equal and == compares the internal state: Ex: string
- In this case, to compare the address, use Object.ReferenceEquals(s1, s2).
- Equal compares the internal state, == compare the address: Ex StringBuilder.
Rule: if you write an equal and == operators, pick one of these meaning (and NOT the 4th one, which is Equal compares address and == compares internal state).
Now, let us see a little bit further...
ReferenceEqual part 1
ReferenceEqual check the identity (same address), and is equivalent to:
Object.ReferenceEqual(v1, v2) <=> ((object) v1) == ((object) v2)
But for the structs, the identity is the identity of the box, thus, for ANY struct c1 and c2 (even if it overloads operators):
Object.ReferenceEquals(c1, c2 ) == false Object.ReferenceEquals(c1, c1 ) == false // <=Look! Object.ReferenceEquals((object)c1, (object)c1) == false object o = c1; Object.ReferenceEquals(o, o ) == true
The reason why it is false is that the structs are boxed in different boxes (expect the last case).
ReferenceEqual part 2 (string.Intern)
If you have different strings that are equal to the same value, ReferenceEqual can return true or false depending of the implementation of the JIT/C# compiler:
string a = "Niels"; string b = "Niels"; Object.ReferenceEquals(a, b ) == false or true (depending of the compilers) !
If you want that some strings that are equal are stored in the same place, use String.Intern.
string a = string.Intern("Hello " + Console.ReadLine());
string b = string.Intern("Hello niels");
Console.WriteLine(object.ReferenceEquals(s1, s2)); // true, if the user write 'niels'.
A good explaination of String.Intern is given in CLR via C#. Note that if a string is interned, the CLR keep a reference to the string, and thus the string will not be garbaged.
Performance
System.ValueType overrides the operator Equals. This implementation compares each field (i.e. it compares the internal state and not the address). Because this implementation uses reflection to achieve it, it is slow. You should override Equals for better performances.
For a standard implementation for Complex numbers (a struct with two doubles), when I override Equals, the code is going two times faster.
Note: you can inherit from IEquatable<double>, to define Equals<double>, which is faster than Equal(object) used with a double. Note: see # Infinity and NaN to see other details on the difference between Equals and ==.
Example (small improvement of [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp%7C the microsoft example]):
public struct Rectangle:IEquatable<Rectangle>{
public double Length, Breadth;
public override bool Equals (object ob) {
if (ob is Rectangle)
return Equals((Rectangle)ob);
else
return false;
}
private bool Equals(Rectangle rect) {
return this.Length == rect.Length && this.Breadth==rect.Breadth;
}
}
GetHashCode
Problem of unicity
GetHashCode must return the same value if it is used twice on the same object. A good implementation should return the same value for two different objects with a very low probability. In other words, most of the time, GetHashCode returns a different value for objects that are not equal.
Now, let's see some problems with GetHashCode. If an object has 4 different states, it seems trivial to produce a hash which is different for different values. But...
class M{
struct Res { public bool fail, success;
}
static void Main(){
Res s; s.fail = false; s.success = true ;
Res f; f.fail = true ; f.success = false;
Console.WriteLine(s.GetHashCode() == f.GetHashCode()); // true...
}
What I have seen in my tests, is that in the default MS 2.0 implementation, GetHashCode returns often the same hash key if the arguments are equal but in a different order. You will have the same behaviour if you implement GetHashCode in this way, because the operator '^' is symmetric:
firstField.GetHashCode() ^ secondField.GetHashCode() ^ thirsdField.GetHashCode()
Note: it is not a big or dangerous mistake. It might just produce more collision in a hash table.
My point is that (with the default MS implementation) the probability that two Hashs are equal for two different objects is not that low. Thus, you should not use it as a unique identifier for your object.
CLR via C# is a very good book. In the 2d edition, it is said that System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(o) returns a unique identifier: the FCL does provide a method that you can call to obtain a unique ID for ann object.[...] It would have been better if Microsoft had named [the method RuntimeHelpers.GetHashCode] something like GetUniqueObjectID. This is NOT true. This piece of code display "[...] given twice [...]" on my PC (framework 2, MS complier & VM):
using System; using System.Collections.Generic;
class M{
class C { public C(int cpt) { this.cpt = cpt; } public int cpt; }
static void Main(){
Dictionary<int, C> all = new Dictionary<int, C>();
for (int i = 0; i < 50000; ++i)
{
C c = new C(i);
int code = System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(c);
if (all.ContainsKey(code))
Console.WriteLine("The code " + code + " has been given twice.");
else
all.Add(code, c);
}
}
So I belive that RuntimeHelpers.GetHashCode returns a 'standard' hash code (i.e. not unique). It can be used if you do not want to call the method o.GetHashCode().
Note that for a given struct, this is false with a high probability(because different boxes):
RuntimeHelpers.GetHashCode(0) == RuntimeHelpers.GetHashCode(0) // 99.99% false
The MS help say that GetHashCode must not throw exceptions. FIXME: I do not know why, I guess it is to allow using it in a catch block.
Conclusion on the unicity:
- Using GetHashCode to have unique identifier is dangerous.
- RuntimeHelpers.GetHashCode(o) returns a hash code which depends of the address of the object. I think it should not be used.
- It sounds stupid to call RuntimeHelpers.GetHashCode on value types.
The algorithm can change
Note that default algorithm of GetHashCode can change (for different versions of the virtual machine, different implementations...). You should not use GetHashCode as MD5. For example, imagine that each time you send an object through the network you send the hash code of this object. The client has to check if it finds the same hash code, so has to check if the message has not been modified. In fact, if the client has a different system, the hash can be different, or if you upgrade you computer, you might change your hash codes.
Simple value types
Difference between FCL types (Int32) and primitives (int)
Almost inexistant. You can imagine that at the beginning of your file you have:
using int = System.Int32; using double = System.Double; using decimal = System.Decimal; ...
One difference is in the declaration of enum, where a primitive type is expected:
enum Bar:int {} // compile.
// enum Bar:Int32 {} does NOT compile.
Int32
For each line, v is equal to Int32.MinValue:
Int32 v;
v = Int32.MinValue;
Int32 i = Int32.MinValue; v = -i; // !
double d = Double.MaxValue; v = (Int32)d; // !
double d = Double.MinValue; v = (Int32)d; // !
double d = Double.NegativeInfinity; v = (Int32)d; // !
double d = Double.PositiveInfinity; v = (Int32)d; // !
double d = Double.NaN; v = (Int32)d; // !
v = Int32.Parse(Int32.MinValue.ToString());
This one is equal to Int32.MaxValue:
v = Int32.MaxValue;
And this one equal to Int32.MinValue+1:
Int32 i = Int32.MaxValue; v = -i;
This one throws an InvalidCastException exception:
v = (Int32)(object)(Int16.MaxValue);
Double and float
If you use 'go to the definition' on Math.Pi, Visual 2005 will open Math[from metadata], with:
public const double PI = 3.14159;
In fact 3.15159 != Math.PI, but ((float)Math.PI) == 3.15159. Thus it might be a bug in the display. The constant Math.PI is more accurate than what is displayed (Math.Pi == 3.141592653589793).
By the way, note that:
Math.PI .ToString() == "3.14159265358979" // By default only 15 digits.
Math.PI .ToString("G17")== "3.1415926535897931" // Internaly, up to 17 digits. (FIXME: check exactly.)
Math.PI .ToString("G51")== "3.1415926535897931" // Can not give more precision...
Math.PI == 3.141592653589793 // 3.141592653589793 does 'not exist' in the double binary format:
3.141592653589793.ToString("G17")== "3.1415926535897931"
In the last case, the number 3.141592653589793 in base 10 is converted into base 2. When it is converted back to base 10 is is slightly different.
FIXME: I tried to find an example (not related to +-inf/nan) in which two doubles are not equal even if one is assigned to the other. I did this in c++ some time ago, the key is to use formula that are processed by the co-processor (that can handle high precision of double:80 bits). If you can find an example, tell me.
0.0, -0.0 and -0
There is a difference between 0.0 and -0.0. This small program shows it clearly:
double plus = 0.0; double minus = -0.0; double fromMinusInt = -0 ; Console.WriteLine(1.0 / plus); // Infinity Console.WriteLine(1.0 / minus); // -Infinity Console.WriteLine(plus == minus); // True (of course!) Console.WriteLine(1.0 / fromMinusInt); // Infinity
For the ints, it is not possible to represent -0 (and it is a good thing that we can not do it!). Here are the bit representation of some Int16 values (the result on Int32 are the same, but longer):
Int16 s; s = Int16.MaxValue; // 0111 1111 1111 1111 s = 5; // 0000 0000 0000 0101 s = 1; // 0000 0000 0000 0001 s = 0; // 0000 0000 0000 0000 s = -1; // 1111 1111 1111 1111 s = -5; // 1111 1111 1111 1011 s = Int16.MaxValue; // 1000 0000 0000 0000
As you can see 1000 0000 0000 0000 does not represent -0. The fact that 0 is coded with the positive numbers, explains why there are 'more' negative values than positive values in integer: Int16.MaxValue = 32767, Int16.MinValue = -32768.
Infinity and NaN
Note that in the following function Sign(Double.NaN) will return -1.0 (and not NaN as you could expect).
double Sign(double a){
if (a > 0.0)
return 1.0;
else if (a == 0.0)
return 0.0; // The following line should be replaced by:
/**/else // /**/else if (Double.IsNaN(a)) return Double.NaN;
return -1.0;
}
The keyword checked will NOT fix the problem.
Let's try a few lines of code:
Console.WriteLine( Double.NaN == Double.NaN ); // False. Console.WriteLine( Double.NaN.Equals(Double.NaN)); // True!
Double are supposed to follow the standard IEC 60559:1989 64-bit float. According to this standard, a Double.NaN is equal to nothing.
If the behaviour is different for Equals, it is because it would conflict with the C# definition of the Equals design pattern (a.Equals(a) should always be true).
Int32.Parse(string) vs Convert.ToInt32(string)
- Int32.Parse(null) throws an exception.
- Convert.ToInt32(null) returns 0.
If the input string is not null, the result is the same.
Is it checked?
static int Add(Int32 i1, Int32 i2) { return i1 + i2; }
static void Main(){
Int32 res = checked(Add(Int32.MaxValue, Int32.MaxValue));
}
... does NOT throw an exception. Because checked is just applied on local variables.
unchecked
{
Decimal resd = Decimal.MaxValue;
resd += resd;
}
... does throw an exception. Decimal is a C# keyword, but is not a primitive type of the CLR. There is no IL instruction to manipulate decimals, thus the check and the throw of the exception is done like in a normal C# member function.
operators does not appear
Let us see the following lines:
foreach (MethodInfo m in typeof(double).GetMethods()) Console.WriteLine(m);
The surprise is that the static operators do not appear. It is possible to add two double using d1 + d2. Thus using the reflection, we could expect to retrieve the corresponding static method (op_Addition). In fact operations on the primitive types are primitives, and are not done through the call of a static method.
Now if you try the same piece of code with Decimal instead of Double, the operators will be displayed.
Note that some .Net languages do not support the Decimal. If these functions were not defined, you would not be able to manipulate decimal easily.
string: new line
Use Environment.NewLine if you need a new line:
string OnWindows = "Hello\r\nWorld"; string OnLinux = "Hello\nWorld"; string OnBoth = "Hello" + Environment.NewLine + "World";
Using the MS C# compiler and JIT of .Net 2, Environment.NewLine is not (really) slower than "\r\n".
Adding characters
Adding two chars produces an Int32, which explains the following behaviour:
Console.WriteLine( 'a'); // displays a letter. // a
Console.WriteLine( 'a' + " displays 1 letter." ); // a displays 1 letter.
Console.WriteLine( 'a' + 'b' + " displays 1 NUMBER!" ); // 195 displays 1 NUMBER!
Console.WriteLine("But " + 'a' + 'b' + " displays 2 letters."); // But ab displays 2 letter.
Note that you can add parenthesis to the third one to display letters:
Console.WriteLine('a' + ('b' + " displays 1 NUMBER? NO")); // ab displays 1 NUMBER? NO
Endiannest
Endiannest is related to byte ordering (see wikipedia endianness).
System.BitConverter.IsLittleEndian returns true if the memory is setup as little endian, anf false if it is as big endian. If your code may run on 'middle endian' computers, you may implement your own test.
Functions that differ only by the returned type
You can notice that casts in C# are the only way to write 'functions' that only differ with the returned type.
class B {}
class C {}
class A
{
public static implicit operator B(A a){ return null;}
public static implicit operator C(A a){ return null;}
}
Special names
In several cases, the C# compiler generates some methods in the class. This is the case for operators, and for properties. It is possible to declare a method with a name that could be a generated one. In the example below, I called a method get_V1; get_V1 is also the name given to a property named V1. If the compiler needs to generate a method with the same name, this yields an error. For example, I can not use the method name get_V2 because this name is already used by the method generated from the property V2.
You can also notice that even get_V2() is generated, it is not possible to call it. In the MSIL, this function has the attribut specialname.
class M
{
static int get_V1() { return 1; }
static int V2 { get { return 2;} }
// static int get_V2() { return 3; } Does not compile if the property V2 is declared.
static void Main()
{
Console.WriteLine(M.get_V1());
Console.WriteLine(M.V2);
// Console.WriteLine(M.V1); Does not compile: get_V1 has NOT a 'special name'.
// Console.WriteLine(M.get_V2()); Does not compile: get_V2 has a 'special name'.
}
}
| C# | special name |
| Property | Property |
|---|---|
| PropertyFoo | get_PropertyFoo and/or set_PropertyFoo |
| this[int i] | get_Item and/or get_Item |
| [IndexerName("MyPropName")]
int this[int i] |
get_MyPropName / set_MyPropName. Renaming is usefull if the indexer is used
from a language which does NOT support indexers, and that Item is not suitable. |
| Operators | Operators |
| operator + (a, b) | op_Addition |
| operator + (a) | op_UnaryPlus |
| operator - (a, b) | op_Subtraction |
| operator - (a) | op_UnaryMinus |
| operator * | op_Multiply |
| operator / | op_Division |
| operator % | op_Modulus |
| operator & | op_BitwiseAnd |
| operator | | op_BitwiseOr |
| operator ^ | op_ExclusiveOr |
| operator << | op_LeftShift |
| operator >> | op_RightShift |
| operator == | op_Equality |
| operator != | op_Inequality |
| operator < | op_LessThan |
| operator <= | op_LessThanOrEqual |
| operator >= | op_GreaterThan |
| operator >= | op_GreaterThanOrEqual |
| Cast | Cast |
| implicit operator | op_Implicit
(note: supports overload with different return types) |
| explicit operator | op_Explicit
(note: supports overload with different return types) |
| Constructor | Constructor |
| MyClass() | .ctor |
| static MyClass() | .cctor |
| ~MyClass() | Finalize |
| Event | FIXME |
| event MyDelegate MyEvent | add_MyEvent and remove_MyEvent |
FIXME: why operators should be public?
Exception
Implement GetObjectData
If you which to use remoting, your exception should have a design close to this one.
Reflexion
GetType().ToString()
GetType().ToString() is for the 'humans', obj.GetType().FullName provides accurate information (ex: Int32 vs System.Int32).
Also AssemblyQualifiedName can be usefull in some cases, it includes the name of the assembly.
Code generation: try other languages
There is two ways to generate code:
- emitting IL. This is painfull.
- emitting C# (or VB). The below section deals about that.
Note that if you emit C#, it is a good practice to try to emit VB once, for testing. In fact the generated C# code may be valide for all your example, but your generator might be incorrect. Emiting another language might show some missing flags (ex: if you implement a method of an interface in a class, C# is more permissive than VB).
Some code are difficult to generate
The CodeDom functions helps a lot to generate code. Using CodeDom, you can generate some parse tree, and the pretty print it in C# or VB, or other language.
Because it is not language specific, there is some limitations. Here are some of them:
- static class: Work around: tag the class as sealed (yourCodeTypeDeclaration.TypeAttributes = TypeAttributes.Sealed).
- condition?a:b: #Ternary if (condition?ifTrue:ifFalse)
- a is A@ #Generating the 'is' operator
- const inside functions. Work around: nothing, or snippet.
- struct constraint: #Generating the struct constraint
- switch: 3 ways: using 'if' (does not work if 'goto case'), using if+label+goto, or using snippet.
Ternary if (condition?ifTrue:ifFalse)
There is no CodeDom function to generate it in a language independent way. It is possible to Generate it using this trick:
// Convert any expression to a string. Language independant.
public static string ExpressionToString(CodeExpression expr, CodeDomProvider provider){
using (TextWriter m = new StringWriter()){
provider.GenerateCodeFromExpression(expr, m, new CodeGeneratorOptions());
return m.ToString();
}}
Then the IfTernary can be built using this method:
// NOT lanugage independant (unless you do some tricks on the provider).
public static CodeExpression IfTernary(CodeExpression booleanExpr,
CodeExpression ifTrue, CodeExpression ifFalse, CodeDomProvider provider)
{
return new CodeSnippetExpression(
"((" + ExpressionToString(booleanExpr, provider) + ") ? (" +
ExpressionToString(ifTrue, provider) + "):("+
ExpressionToString(ifFalse, provider) + "))");
}
Generating the 'is' operator
Quick and durty solution
The same trick that the ternary if can be used (see previous section for the implementation of ExpressionToString):
public static CodeExpression Is(CodeVariableReferenceExpression var,
CodeTypeReferenceExpression type, CodeDomProvider provider)
{
return new CodeSnippetExpression(
"((" + ExpressionToString(var, provider) + ") is " +
ExpressionToString(type, provider) + ")");
}
Second solution language independant
FIXME: Change IsAssignableFrom to IsInstanceOfType
The following solution seems to be language independant. It uses the method IsAssignableFrom. Please read the remarks below.
// creates: Object.ReferenceEquals(var, null) == false &&
// typeof(type).IsAssignableFrom(var.GetType())
public static CodeExpression Is(CodeVariableReferenceExpression var,
CodeTypeReference type,
CodeDomProvider provider)
{
CodeExpression isNull = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(typeof(object)),
"ReferenceEquals",
var,
new CodePrimitiveExpression(null));
CodeExpression isNotNull = new CodeBinaryOperatorExpression(isNull,
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression(false));
CodeExpression isAssignable = new CodeMethodInvokeExpression(
new CodeTypeOfExpression(type),
"IsAssignableFrom",
new CodeMethodInvokeExpression (var, "GetType"));
return new CodeBinaryOperatorExpression(isNotNull,
CodeBinaryOperatorType.BooleanAnd,
isAssignable);
}
Notes:
- I am not sure that it is language independant. It supposes that, for all languages, the second part of a boolean && condition is NOT evaluated if the first one is false (true in C# and C, C++, Java).
- This methods might hide some C# warnings.
- It is difficult to read the generated code.
- It should be improved (example: by generating different code for reference and value types).
Third solution
The third solution is to use a static function which performs the test. Something like:
static class Helper {
public static bool Is<Type>(object o) { return o is Type; }
public static bool Is(object o, Type t) { return o != null && t.IsAssignableFrom(o.GetType());}
}
Notes:
- The second one is for languages that do not support generics.
- It is easier to read than the previous version.
- The non-generic function 'Is' is language independant: on contrary to the second (because it is compiled in C#, and stored in an assembly, which is not the case of the second solution, which is encoded directly in the generated code (and thus might be in another language).
- The generated code should call Helper.Is. This is not really nice, because it requires to reference the assemblies which defines 'Is'.
Generating the struct constraint
I could not find a clean way to generate the struct constraint, like:
class MyStruct<T>: where T: struct, System.ICloneable
{}
Note that it is is easy to add interfaces or a new constraint. Here is what I tried, and did how it worked:
Constraints.Add(typeof(ValueType)); // Generates invalid code.
Constraints.Add("struct"); // Generates @struct
// Try to use CodeSnippetTypeDeclaration; Do not exist.
Constraints.Add(" struct"); // It works!! See the note below.
Note that this behaviour is likely to be non standard or might be prohibed in the future.
Example:
CodeTypeParameter param = new CodeTypeParameter("T");
// Note the space before 'struct'.
param.Constraints.Add(" struct");
// other constraints should be AFTER:
param.Constraints.Add(typeof(ICloneable));
CodeTypeDeclaration t = new CodeTypeDeclaration("MyType");
t.TypeParameters.Add(param);
Equivalent to using bloc
using (StreamReader r = Build())
{ // [Code A]
}
Is equivalent to:
StreamReader r = Build();
try{
//[Code A]
}
finally{
if (r != null)
((System.IDisposable)r).Dispose();
}
FIXME: talk about the fact that r become readonly. See Compiler-generated scopes for local variable declarations of Sam NG blog
Equivalent to lock
lock (o)
{
// [Code B]
}
is equivalent to:
System.Object cpy__ = o;
System.Threading.Monitor.Enter(cpy__);
try{
// [Code B]
}
finally{
System.Threading.Monitor.Exit(cpy__);
}
Equivalent to foreach
FIXME: double check that. double check E.
FIXME: if enumerator is outside of the scope...
collection myCollection = foobar();
foreach (ElementType element in myCollection)
{statement;}
is equivalent to:
{
E enumerator = (collection).GetEnumerator();
try
{
while (enumerator.MoveNext())
{
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally
{
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
FIXME: have a look at "It is important to emphasize that iteration variables of for' and foreach loops are instantiated only once, regardless of a number of iterations, though their scope extends only over the embedded statement. This rule has effect on closures." (nikov). "The iteration variable is only instantiated once, and is reassigned to for each iteration of the loop" (sam). FIXME: do this test: in a foreach loop, use an element type that has a finilizer with hello world in it. In the statements, call GC. check what is correct, the code, or the comments above.
Optimisation
FIXME tests on StringBuilder.Append
Important note: the result of these tests might be very different from one C# compiler to another, and also different from one CLI compiler to another. The goal is to understand why it is slower or faster, and not to optimize the code to make it faster (but difficult to write / not natural).
I have done some test on the method Append of the StringBuilder.
Each test has been done using a code close to:
Timer.Start("concat"); // My own timer. You do not have this class.
int n = 50000;
for (int i = 0; i < n; ++i)
{
StringBuilder b = new StringBuilder();
for (int j = 0; j < 1000; ++j)
XXXXXX; //Function tested.
}
Timer.End("concat");
| X | b.Append(j + ",") | b.Append(j.ToString() + ",") | b.Append(j).Append(",") | b.Append(j.ToString()).Append(",") |
|---|---|---|---|---|
| time | 17.70 | 16.08 | 15.68 | 15.09 |
The last consums 15% less time than the first one. Some keys to understand why:
- FIXME: to be completed. Read and compare the asm.
- Append(j).Append(",") is faster than b.Append(j.ToString() + ","). One reason: it avoids the copy the string which contains the value of j (the int j is directly written in the overload Append(int)).
- Note that j.ToString() does not box the int j.
String are not that slow
On some forums, it is said that strings are slow, and that concatenations using '+' should be avoided (and transformed using StringBuilder). This is sometime true if the concatenation is done in a loop, but otherwise the compiler V2 of MS optimizes well the code. Note that StringBuilder prevent a lot of optimisations. Here is an example of how the code is optimized by the MS compiler:
string foo = "one"; string s = foo + 2.ToString() + "three" + "four" + "five";
Produce the following MSIL:
IL_0000: ldstr "one"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.2
IL_0008: stloc.1
IL_0009: ldloca.s V_1
IL_000b: call instance string [mscorlib]System.Int32::ToString()
IL_0010: ldstr "threefourfive" // <= concatenation already done
IL_0015: call string [mscorlib]System.String::Concat(string, // <= only one function is called.
string, // so there will be no unnecessary object.
string)
Native image Generator Ngen.exe
It compiles the assemblies. It is provided by the .Net Framework. You might want to use it to reduce the start-up time. But do not forget that it might produce lowest performance than just in time compilation. Example:
- in JIT compilation, the address of the static variable can be hard coded (not the case for standard binaries).
- it will not perform all the optimisations based on the hardware (so has to work after you change the processor).
According to CLR via c# your code can be 5% slower than JIT code (but in some case it can be faster).
Here is a good introduction to NGen: [NGen Revs Up Your Performance with Powerful New Features]
I am not sure, but I think there are several issues if you want to release that kind of binary.
Debug
FIXME:
- DebuggerDisplay
- DebuggerStepThrough
Installation, assembly
PEVerify tool (Peverify.exe)
PEVerify check if an assembliy and its references (and the references of the references...) are 'safe'. This might be necessary in some contexts (ex: in a browser).
ClsCompliance
All .Net languages support the CLS (common language subset). For example, the type Int32 is in the CLS, thus supported by all languages. On the other hand, UInt32 (unsigned int) or delegates are not in the CLS. Thus, if an UINT32 is returned some languages will not be able to call your function. Other common issues are members that differ only by the case (not supported by VB). To check the conformance of your code you can use the CLSCompliantAttribute:
using System; [assembly: CLSCompliant(true)]
Excel
I write another long and technical page on how to write an Excel Add-In and Com Server in C#. This pages explains a lot of problems and solutions.
Windows processes
This section has nothing to do on this page. Sorry.
- alg.exe
- Application Layer Gateway. Firwall, shared connection.
- csrss.exe
- Client/Server Runtime Subsystem. GUI.
- ctfmon.exe
- Alternative User Input Services. Speech/written/etc recognition.
- explorer.exe
- Windows Program Manager or Windows Explorer. Windows Graphical Shell (includes Start menu, taskbar, desktop, and File Manager).
- lsass.exe
- Local Security Authority Subsystem Service. Identification, deal whith winmon.exe
- nvsvc32.exe
- NVidia Service 32-bit
- rundll32.exe
- Run a DLL as a 32-bit application. Managed load/unload of Dlls.
- services.exe
- Windows Service Controller. Change of hardware configuration.
- smss.exe
- Session Management Subsystem. 1st process created in user mode. Use to manage sessions.
- spoolsv.exe
- Printer Spooler Service
- svchost.exe(xN)
- Service Host Process. Run dll services. At startup, run the services in HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Svchost.
- System
- FIXME
- taskmgr.exe
- task Manager
- winlogon.exe
- Windows LogOn Process. Also used by Ctrl-Alt-Supr.
- systeme idle process (processus inactif du système)
- Issues the instruction to reduce the power of the processor
Windows Registry structure
This section should not be on this page...
- HKEY_CLASSES_ROOT
- File association. Merge of HKEY_LOCAL_MACHINE\SOFTWARE\Classes and 'HKEY_CURRENT_USER\SOFTWARE\Classes (this last one is also an alias).
- HKEY_CURRENT_USER
- Info about the user currently logged. Sub key of the user's root key in the HKEY_Users.
- HKEY_CURRENT_CONFIG
- Current hardware configuration (can change if multiple hardware conf). Sub key of HKEY_LOCAL_MACHINE (sub key or merge ? I am not sure).
- DYN_DATA
- Dynamic information about your server. You should not mess with it.
- HKEY_LOCAL_MACHINE
- root
- HKEY_System
- root
Things that I do not know
If you find the answer, please change this page or send an email to "niels AT nvv DOT name".
- Why volatile can not be applied on double (but can be applied on float)?
- Write a design pattern to have a method F(int arg1, int arg2) callable only from derived function in the same assembly (see
- Create an object without calling the CTOR (and without using serialization or MemberWiseClone).
- Constructor in the interface: it is possible in the CLR (not in C#). Should test it.
For any remark on this page, email "niels AT nvv DOT name".