- 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
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):
- Creating and using an assembly
- 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