Krzysztof Mossakowski
For Students
  • Console application generated by Visual Studio (New Project / Visual C# / Windows / Console Application
    • References to System, System.Data, System.XML are not needed for this application
    • Source files
      • Program.cs
      • Properties\AssemblyInfo.cs
      • App.ico
    • After compilation
      • Two created folders: bin, obj
      • The executable file:
        • only 16 KB!
        • some properties visible in Explorer (title, description, company, product, version etc.)
    • Practice
      • Add the following code in Main() method
      •  int i, j = 2;
         i = 3;
         i = i + j;
         i = i * 4;
         Console.WriteLine( "Hello, World! - [i = {0}]", i );
         Console.ReadLine();			
 
  • Decompilation
    • Run Visual Studio .NET 2005 Command Prompt (from "Visual Studio Tools" group in "Microsoft Visual Studio 2005" from menu Start)
    • Run ildasm.exe
      • Open your executable file (*.vshost.exe is a special file used by Visual Studio 2005 to improve debugging performance))
      • See Microsoft Intermediate Language representation of your source code (find Main() method)
    • Run Lutz Roeder's .NET Reflector, original link: http://www.aisto.com/roeder/dotnet/
      • Use .NET Framework 2.0.* (Compact Framework is used for mobile solutions)
      • Open your executable file
      • Look at code of your application presented in some available .NET languages
      • Try to guess meanings of MSIL instructions from the view of IL code
    • Run an obfuscator tool
      • Dotfuscator Community Edition (from "Visual Studio Tools" group in menu Start or from Tools menu from Visual Studio)
        • Add your executable to list of project's assemblies
        • Build project
        • Look at obfuscated executable in the reflector
 
  • More decompilation - some interesting issues of MSIL and C# (insert source codes listed below, compile and decompile)
    • Using strings
         string s = "123";
         s = 123.ToString();
         for ( int i = 0; i < 10; i++ )
         {
             s += '!';
         }
         Console.WriteLine( s ); 
         Console.ReadLine();
      • Question: why is this code wrong?
      • Conclusion: use StringBuilder class for modifying strings
    • Boxing, unboxing and generics
         ArrayList myInts = new ArrayList();
         myInts.Add(10);
         Console.WriteLine("The value: {0}", (int)myInts[0]);
         Console.ReadLine();
      • Consider box and unbox commands in MSIL
      • Conclusion: use Generics (introduced in .NET Framework 2.0)
      •  List<int> myBetterInts = new List<int>();
         myBetterInts.Add(10);
         int v = myBetterInts[0];
    • Overloading operators
         class Point
         {
             int x, y;
             public int X { get{ return x; } set { x = value; }}
             public int Y { get{ return y; } set { y = value; }}
             public Point(int setX, int setY) { x = setX; y = setY; }
             public static Point operator +(Point p1, Point p2) { return new Point(p1.x + p2.x, p1.y + p2.y); }
             public static Point operator ++(Point p1) { return new Point(p1.x + 1, p1.y + 1); }
         }
         
         class Program
         {
             static void Main(string[] args)
             {
                 Point pt1 = new Point(1, 2);
                 Point pt2 = new Point(3, 4);
                 Point pt3 = pt1 + pt2;
                 pt3 += pt2;
                 Console.WriteLine("pt3: ({0}, {1})", pt3.X, pt3.Y);
                 Console.ReadLine();
             }
         }
      • See MSIL representation of overloaded operators
    • Loops
         List<int> list = new List<int>();
         for (int i = 0; i < 10000000; i++) {
             list.Add(i);
             list.Add(-i);
         }
         int sum = 0;
         DateTime t1, t2;
        
         int size = list.Count;
         int myInt2;
         t1 = DateTime.Now;
         for (int i = 0; i < size; i++) {
             sum += list[i];
         }
         t2 = DateTime.Now;
         Console.WriteLine("for: {0} milliseconds", (t2 - t1).Milliseconds);
        
         int i2 = 0;
         t1 = DateTime.Now;
         while (i2 < size) {
             sum += list[i2];
             i2++;
         }
         t2 = DateTime.Now;
         Console.WriteLine("while: {0} milliseconds", (t2 - t1).Milliseconds);
        
         t1 = DateTime.Now;
         foreach (int myInt in list) {
             sum += myInt;
         }
         t2 = DateTime.Now;
         Console.WriteLine("foreach: {0} milliseconds", (t2 - t1).Milliseconds);
      • Run and compare times of looping
      • Analyse MSIL code to check why foreach loop is the slowest
    • Classes and structs
         class ClassPoint {
            public int x, y;
            public ClassPoint(int setX, int setY) {x = setX; y = setY;}
         }
         struct StructPoint {
            public int x, y;
            public StructPoint(int setX, int setY) {x = setX; y = setY;}
         }
        
         class Program
         {
            static ClassPoint ClassFun(ClassPoint pt1, ClassPoint pt2)
            {
                ClassPoint pt3 = new ClassPoint(pt1.x + pt2.x, pt1.y + pt2.y);
                pt1.x++; pt1.y++; pt2.x++; pt2.y++;
                return pt3;
            }
        
            static StructPoint StructFun(StructPoint pt1, StructPoint pt2)
            {
                StructPoint pt3 = new StructPoint(pt1.x + pt2.x, pt1.y + pt2.y);
                pt1.x++; pt1.y++; pt2.x++; pt2.y++;
                return pt3;
            }
        
            static void Main(string[] args)
            {
                ClassPoint cpt1 = new ClassPoint(1, 2);
                ClassPoint cpt2 = new ClassPoint(3, 4);
                ClassPoint cpt3 = ClassFun(cpt1, cpt2);
                Console.WriteLine("cpt1: ({0}, {1}), cpt1: ({2}, {3}), cpt1: ({4}, {5})",
                    cpt1.x, cpt1.y, cpt2.x, cpt2.y, cpt3.x, cpt3.y);
        
                StructPoint spt1 = new StructPoint(1, 2);
                StructPoint spt2 = new StructPoint(3, 4);
                StructPoint spt3 = StructFun(spt1, spt2);
                Console.WriteLine("spt1: ({0}, {1}), spt1: ({2}, {3}), spt1: ({4}, {5})",
                    spt1.x, spt1.y, spt2.x, spt2.y, spt3.x, spt3.y);
            }
         }
      • Remember: classes are passed by reference, structs are passed by value (unless ref keyword is used)
    • Delegates
         public delegate string MyDelegate(bool a, bool b, bool c);
         public delegate string MyOtherDelegate(out bool a, ref bool b, int c);
         public delegate int BinaryOp(int x, int y);
        
         public class SimpleMath {
            public int Add(int x, int y) { return x + y; }
            public int Subtract(int x, int y) { return x - y; }
         }
        
         class Program
         {
            static void Main(string[] args)
            {
                SimpleMath m = new SimpleMath();
                Console.Write("What (+,-)?");
                char ch = (char)Console.Read();
                BinaryOp bop = null;
                if (ch == '+') {
                    bop = new BinaryOp(m.Add);
                } else if (ch == '-') {
                    bop = new BinaryOp(m.Subtract);
                }
        
                if (bop != null) {
                    Console.WriteLine("bop(10, 5) is {0}", bop(10, 5));
                }
                Console.ReadLine();
            }
         }
      • Note that for each defined delegate one sealed class is created in MSIL.
    • Events
         class MyClass
         {
            public delegate void MyEventHandler(string p);
            public event MyEventHandler MyEvent;
            public void doIt()
            {
                if (MyEvent != null) {
                    MyEvent.Invoke("doIt() from MyClass");
                }
            }
         }
        
         class Program
         {
            static void fun1(string p)
            {
                Console.WriteLine("fun1: {0}", p);
            }
            
            static void Main(string[] args)
            {
                MyClass mc = new MyClass();
                mc.MyEvent += new MyClass.MyEventHandler(fun1);
                mc.MyEvent += delegate(string p) {
                    Console.WriteLine("anonymous delegate: {0}", p);
                };
                mc.doIt();
                Console.ReadLine();
            }
          }
      • Check MSIL implementation of events.
 
  • Source code XML documentation
    • Insert an empty line before a method or a class and start typing ///
    • Find in MSDN information about XML documentation (Development Tools and Languages / Visual Studio / Visual C# / C# Programming Guide / XML Documentation Comments or online).
    • Check the option "XML documentation file" in project's properties / Build, and provide the name of target file.
    • Compile the project - the file with XML documentation will be created (check it in bin/debug directory).
    • Create HTML documentation files using one of available tools (unfortunately there is no such tool in Visual Studio 2005):
      • NDoc - open source
      • Sandcastle - by Microsoft
 
  • Creating and using an assembly
    • Add new project named 'My Assembly' of type 'Class Library' to the solution
      • Modify the name of the class in MyAssembly to 'MyAssemblyClass' (test Refactor / Rename opion in Visual Studio)
      • Create public method Hello() returning "Hello from MyAssembly" string
    • Call Hello() method of MyAssemblyClass from Main() method
      • Replace the code in Main() method with the following
      •  MyAssemblyClass myAssemblyClass = new MyAssemblyClass();
         Console.WriteLine( myAssemblyClass.Hello() );
      • Note that IntelliSense does not work for MyAssemblyClass class
      • Try to compile: The type or namespace name 'MyAssemblyClass' could not be found (are you missing a using directive or an assembly reference?)
      • Add a reference to MyAssembly in the main project
        • Use 'Add Reference' option (e.g. right click on the project or 'References' option in Solution Explorer)
        • Use 'Projects' tab and select MyAssembly
      • Use a namespace created in MyAssembly (using MyAssembly;)
    • Compile the solution
      • Note that the assembly's file (dll) is copied into bin folder of the main project
 
  • Reflection
    • Add the following code to the class with Main() method
    •  static void ShowMethodInfo( MethodInfo method ) 
       {
      	Console.Write( "      " + method.ReturnType + " " + method.Name + "(" );
      	ParameterInfo[] parameters = method.GetParameters();
      	for ( int i = 0; i < parameters.Length; i++ ) {
      		if ( i > 0 ) {
      			Console.Write( "," );
      		}
      		Console.Write( parameters[i].ParameterType + " " + parameters[i].Name );
      	}
      	Console.Write( ")" );
      	if ( method.IsStatic ) {
      		Console.Write( " |static" );
      	} 
      	if ( method.IsVirtual ) {
      		Console.Write( " |virtual" );
      	}
      	if ( method.IsPublic ) {
      		Console.Write( " |public" );
      	}
      	Console.WriteLine();
       }
      
       static void ShowTypeInfo( Type type ) 
       {
      	Console.WriteLine( "    " + type.Name );
      	Console.WriteLine( "    BaseType: " + type.BaseType.Name );
      	MethodInfo[] methods = type.GetMethods();
      	for ( int i = 0; i < methods.Length; i++ ) {
      		ShowMethodInfo( methods[i] );
      	}
       }
      
       static void ShowAssemblyInfo( Assembly assembly ) 
       {
      	Console.WriteLine( "ShowAssemblyInfo" );
      	Console.WriteLine( "  FullName: " + assembly.FullName );
      	Console.WriteLine( "  GAC: " + assembly.GlobalAssemblyCache );
      	Console.WriteLine( "  Location: " + assembly.Location );
      	Type[] types = assembly.GetTypes();
      	for ( int i = 0; i < types.Length; i++ ) {
      		ShowTypeInfo( types[i] );
      	}
      	Console.Write( "  AssemblyDescription: " );
      	object[] attrs = assembly.GetCustomAttributes( true );
      	AssemblyDescriptionAttribute ada = null;
      	for ( int i = 0; i < attrs.Length; i++ ) {
      		ada = ( attrs[i] as AssemblyDescriptionAttribute );
      		if ( ada != null ) {
      			Console.WriteLine( ada.Description );
      			break;
      		}
      	}
       }
    • Practice: make the code to compile successfully
    • Add the following code to the Main() method
    •  Assembly assembly = Assembly.GetAssembly( myAssemblyClass.GetType() );
       ShowAssemblyInfo( assembly );
    • Run the project and note the following issues:
      • Version number and culture
      • Location
      • All methods inherited from Object class
 
  • Global Assembly Cache (GAC)
    • Look at GAC
      • gacutil.exe /l (from Visual Studio .NET 2005 Command Prompt)
      • mscorcfg.msc (from Visual Studio .NET 2005 Command Prompt) or 'Microsoft .NET Framework 1.1 Configuration' administrative tool
      • %Systemroot%\assembly folder (e.g. on Windows XP: C:\WINDOWS\assembly)
    • Create strong-named assembly
      • Add new project named 'MyGlobalAssembly' of type 'Class Library' to the solution
      • Modify the name of the class in MyAssembly to 'MyGlobalAssemblyClass'
      • Create public method Hello() returning "Hello from MyGlobalAssembly" string
      • Create a key pair (necessary to sign an assembly to allow to install it in GAC): in project's folder run command using Visual Studio .NET 2005 Command Prompt: sn.exe -k key.snk
        • the file key.snk should appear in project's folder
      • Sign the assembly using Signing / Sign the assembly from project's properties (check the checkbox and choose created key.snk file as a strong name key file)
      • Install assembly in the GAC: gacutil.exe -i MyGlobalAssembly.dll (in bin/debug folder)
        • Look at the GAC and check visible assembly's properties
    • Use the assembly from GAC
      • At compile time: add reference to the project (when the assembly from GAC is a part of your current solution, it is better to install it into 'Program Files\Microsoft Visual Studio 8\Common7\IDE\Public Assemblies' directory to be able to see it in 'Add Reference' window in Visual Studio (see MSDN for details))
      • At run time: (version number and public key take from the GAC)
      •  assembly = Assembly.Load(
         	"MyGlobalAssembly,Version=1.0.0.0,Culture=neutral,PublicKeyToken=9e548ef0159c0b7b");
        (make sure to replace PublicKeyToken with value from your assembly)
 
  • Debugging
    • Conditional compilation constants: DEBUG, TRACE (project's properties / Build)
    •  #if DEBUG 
      	... 
       #endif
    • System.Diagnostics.Debug class - possibility to write debug messages to any registered listener (e.g. a console or text file)
       TextWriterTraceListener tr1 = new TextWriterTraceListener(System.Console.Out);
       Debug.Listeners.Add(tr1);
      
       TextWriterTraceListener tr2 = new TextWriterTraceListener(System.IO.File.CreateText("Output.txt"));
       Debug.Listeners.Add(tr2);
      
       Debug.WriteLine("sample debug text");
      
       Debug.Flush();
    • Assertions (conditionals stop of application running in debug mode)
       object obj = null;
       Debug.Assert(obj != null);
    •  Debug.Assert( log.ToUpper() != "APPLICATION");
    • System.Diagnostics.ConditionalAttribute - calls to conditional methods are removed by the compiler
    •  [System.Diagnostics.Conditional("DEBUG")]
       void HelloWorld()
       {
      	Console.WriteLine("Hello World");
       }
    • System.Diagnostics.EventLog - Event Viewer (administrative tool) - eventvwr.msc
    •  // Create the source, if it does not already exist.
       if(!EventLog.SourceExists("MySource")){
      	EventLog.CreateEventSource("MySource", "MyNewLog");
      	Console.WriteLine("CreatingEventSource");
       }
       // Create an EventLog instance and assign its source.
       EventLog myLog = new EventLog();
       myLog.Source = "MySource";
       // Write an informational entry to the event log.    
       myLog.WriteEntry("Writing to event log.");
    • System.Diagnostics.PerformanceCounter - Performance (administrative tool) - perfmon.msc