Very brief look at C#

Wiktor Zychla

C# ( pronounced like see sharp ) is a new great programming language from Microsoft. It is a kind like Java but several common Java flaws were fixed (for example lack of enums, lack of the ability to override operators, lack of function pointers). C# sources are compiled into the intermediate language IL and in the runtime the C# virtual machine compiles IL into the native code. Just like in other modern programming languages, in C# you do not write methods to destroy objects. Instead, the environment use so called garbage collector that inspects all objects and frees the memory for unused ones.

The .NET Framework is everything you need to build your own C# applications. The Framework can be downloaded from Microsoft HomePage and it is approximately 20MB of data. There's also a richer version that is called .NET Framework SDK (about 120MB) with a tons of examples and full docs. Both versions are FREE to use.

After succesful instalation, .NET Framework places itself into the special directory in the SYSTEM subdirectory and then you are able to invoke csc.exe that is C# compiler.

The exists an open-source project that heads to moving C# to Linux. More info at the project homepage: go-mono.

The simplest C# program could be like this one:


using System;

namespace Project1
{
	public class Ex1
	{
		public static void Main(string[] args)
		{
			Console.WriteLine("The first C# program!");
		}
	}
}

Just type the source into the file named ex1.cs and invoke the compiler csc.exe ex1.cs.

The compiler answer is:


D:\EXAMPLE1>C:\WINNT\Microsoft.NET\Framework\v1.0.3705\csc.exe ex1.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.

D:\_
and the output file is 3072 bytes long.

C# object model is really close to SmallTalk model, where every object inherits from the most general type called "object". The type "object" implements a few methods and thus all objects inherit those methods. One of them is called "ToString()" and is really useful. It is used to convert an object to its string representation. Let's see a trivial example:


using System;

namespace Project1
{
	public class A
	{
		public int field;
		public A() {}
	}

	public class Ex1
	{
		public static void Main(string[] args)
		{
			A a     = new A();
			a.field = 7;
			Console.WriteLine( a.ToString() );
		}
	}
}
The output of the program is:

Project1.A
This is just because if you do not override the ToString() method, it always returns the name of the type. Let's then fix our example:

(...)
	public class A
	{
		public int field;
		public A() {}

		public override string ToString()
		{
			return "I am an object of type A and I hold the value " + field.ToString();
		}
	}
As expected, the output is:

I am an object of type A and I hold the value 7
Why we do not have to override the ToString() method for type int when we call field.ToString()? Simple, it has already been overridden.

So, how to convert the string value to int value? This is also simple. The type "int" has a static method "Parse()" that you can use.


using System;

namespace Project1
{
	public class Ex1
	{
		public static void Main(string[] args)
		{
			string  a = "456";
			int     b = int.Parse(a);
			Console.WriteLine( b.ToString() );
		}
	}
}
The same idea can be used to parse string into the floating point types.

The great idea of C# is that you can obtain an instance of the compiler itself and be able to emit code in runtime. The following example is a really simplest compiler written in C#. It can emit executables but it can also compile the source to memory and invoke methods from memory. That's how C# can be used as script language.

The example is quite complicated because it uses the System.Windows.Forms namespace that allows you to create windows and other visual components. Note that unlike in many languages, in C# the design of all components is really a part of source code! For the more detailed reference of this namespace, refer to any C# book, for example:

/* Wiktor Zychla, 2002 */
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Diagnostics;
using System.Threading;
using System.Reflection;

namespace SimpleCSCompiler
{
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.TextBox textBox1;
		private System.Windows.Forms.Button button1;
		private System.Windows.Forms.TextBox textBox2;
		private System.Windows.Forms.Button button2;
		private System.Windows.Forms.Button button3;
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			InitializeComponent();
		}

		/// 
		/// Clean up any resources being used.
		/// 
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

      #region Windows Form Designer generated code
		/// 
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// 
		private void InitializeComponent()
		{
			this.textBox2 = new System.Windows.Forms.TextBox();
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.button1 = new System.Windows.Forms.Button();
			this.button2 = new System.Windows.Forms.Button();
			this.button3 = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// textBox2
			// 
			this.textBox2.BackColor = System.Drawing.SystemColors.Control;
			this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None;
			this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.textBox2.ForeColor = System.Drawing.SystemColors.WindowText;
			this.textBox2.Location = new System.Drawing.Point(248, 8);
			this.textBox2.Multiline = true;
			this.textBox2.Name = "textBox2";
			this.textBox2.Size = new System.Drawing.Size(240, 240);
			this.textBox2.TabIndex = 2;
			this.textBox2.Text = "";
			// 
			// textBox1
			// 
			this.textBox1.Location = new System.Drawing.Point(4, 8);
			this.textBox1.Multiline = true;
			this.textBox1.Name = "textBox1";
			this.textBox1.Size = new System.Drawing.Size(240, 240);
			this.textBox1.TabIndex = 0;
			this.textBox1.Text = "write the C# code here and use buttons below";
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(4, 252);
			this.button1.Name = "button1";
			this.button1.TabIndex = 1;
			this.button1.Text = "Build";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// button2
			// 
			this.button2.Location = new System.Drawing.Point(84, 252);
			this.button2.Name = "button2";
			this.button2.TabIndex = 1;
			this.button2.Text = "Run";
			this.button2.Click += new System.EventHandler(this.button1_Click);
			// 
			// button3
			// 
			this.button3.Location = new System.Drawing.Point(4, 280);
			this.button3.Name = "button3";
			this.button3.Size = new System.Drawing.Size(76, 36);
			this.button3.TabIndex = 3;
			this.button3.Text = "Run In Memory?";
			this.button3.Click += new System.EventHandler(this.button3_Click);
			// 
			// Form1
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(508, 321);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.button3,
																		  this.button2,
																		  this.textBox2,
																		  this.button1,
																		  this.textBox1});
			this.Name = "Form1";
			this.Opacity = 0.89999997615814209;
			this.Text = "The simple compiler written in C#";
			this.ResumeLayout(false);

		}
      #endregion

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

		private void button1_Click(object sender, System.EventArgs e)
		{
			CSharpCodeProvider codeProvider = new CSharpCodeProvider();
			ICodeCompiler icc = codeProvider.CreateCompiler();
			string Output = "Out.exe";
			Button ButtonObject = (Button) sender;

			textBox2.Text = "";
			System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
			parameters.GenerateExecutable = true;
			parameters.OutputAssembly = Output;
			CompilerResults results = icc.CompileAssemblyFromSource(parameters,textBox1.Text);

			if (results.Errors.Count > 0)
			{
				textBox2.ForeColor = Color.Red;
				foreach(CompilerError CompErr in results.Errors)
				{
					textBox2.Text = textBox2.Text +
						"Line number " + CompErr.Line + 
						", Error Number: " + CompErr.ErrorNumber + 
						", '" + CompErr.ErrorText + ";" + 
						Environment.NewLine + Environment.NewLine;
				}
			}
			else
			{
				textBox2.ForeColor = Color.Blue;
				textBox2.Text = "Success!";
				if (ButtonObject.Text == "Run") Process.Start(Output);            
			}
		}

		private void button3_Click(object sender, System.EventArgs e)
		{
			CSharpCodeProvider codeProvider = new CSharpCodeProvider();
			ICodeCompiler icc = codeProvider.CreateCompiler();
			Button ButtonObject = (Button) sender;

			textBox2.Text = "";
			System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
			parameters.GenerateExecutable = false;
			parameters.GenerateInMemory = true;
			CompilerResults results = icc.CompileAssemblyFromSource(parameters,textBox1.Text);

			if (results.Errors.Count > 0)
			{
				textBox2.ForeColor = Color.Red;
				foreach(CompilerError CompErr in results.Errors)
				{
					textBox2.Text = textBox2.Text +
						"Line number " + CompErr.Line + 
						", Error Number: " + CompErr.ErrorNumber + 
						", '" + CompErr.ErrorText + ";" + 
						Environment.NewLine + Environment.NewLine;
				}
			}
			else
			{
				textBox2.ForeColor = Color.Blue;
				textBox2.Text = "Success!";

				Assembly   assembly = results.CompiledAssembly;
				Type   t = assembly.GetType("AXXX.ABC");

				MethodInfo me = t.GetMethod("Alabama");

				/*
				// list all methods
				foreach( MemberInfo mem in t.GetMethods() )
					MessageBox.Show ( mem.Name.ToString() );
				*/

				object result;
				result = t.InvokeMember("Clabama", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod ,
					 null, null, null );				
				MessageBox.Show ( ((int)result).ToString() );
            
			}
		
		}

	}

}

/*
  // test the compiler with this code
using System;

namespace AXXX
{
	class ABC
	{
		public static int[] Alabama()
		{
			int []i = new int[2];
			i[0] = 78;
			return i;
		}
		public static string Blabama()
		{
			return "alamakota";
		}
		public static int Clabama()
		{
			return 56;
		}
	}
}
*/

If your program is really complex and consists of lot of source files, csc.exe must be fed with a lot of parameters. This simple tool can be a solution to this problem. I've called it edPCS. You just create a simple XML file that keeps all the info about the project and feed edPCS.exe with the name of XML file. All compilation is done by invoking csc.exe. The tool can be for example used in connection with EditPlus, the famous text editor. Just map edPCS.exe as the user tool, create a project, fill the data in XML file and use edPCS.exe as a compiler.


	/*
	 * File: edPCS.cs
	 * 
	 * (c) 2002 Wiktor Zychla, torq314@wp.pl
	 * Feel free to modify&develop thit tool.
	 * 
	 * A very simple command line tool that 
	 * helps to compile *.cs files from command line.
	 *  
	 * You no longer need to run csc.exe with tons of parameters, 
	 * instead you run edPCS.exe that 
	 * a) reads a configuration file where you put info about:
	 *    - .NET framework path
	 *    - project path 
	 *    - source files
	 *    - compiler switches
	 * b) invokes csc.exe with a full command line including
	 *    all needed parameters
	 * 
	 * Suppose you'd like to use EditPlus as your C# editor.
	 * You just have to:
	 * 1. compile this source
	 * 2. put the edPCS.exe somewhere
	 * 3. run EditPlus
	 * 4. configure edPCS.exe as a new user tool
	 * 5. create a new project
	 * 6. add a special XML file for the project containing
	 *    enough info to run a compiler
	 * 7. run edPCS.exe from EditPlus on the XML file
	 * 
	 * XML file format:
	 * 
	 * C:\WinNT\Microsoft NET Framework
	 * D:\Project Path
	 * /nologo /target:exe 
	 * file1.cs
	 * file2.cs
	 * file3.cs
	 * 
     */

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.XPath;

namespace edPlusCSharp
{
	class cClass
	{
		static string    sFWPath;
		static string    sProjPath;
		static string    sSwitch;
		static ArrayList sSourceFiles = new ArrayList();

		static void Main(string[] args)
		{
			try
			{
				ParseDataFile( args[0] );			
				string answer = CompileFiles();
				Console.WriteLine ( answer );
			}
			catch ( System.IndexOutOfRangeException ex )
			{
				Console.WriteLine( "A simple tool that helps to compile *.cs files from EditPlus" );  
				Console.WriteLine( "Usage: EXENAME xmldata.xml" );
			}
			catch ( Exception ex )
			{
				Console.WriteLine( "Exception caught. " + ex.Message );
			}
		}

		static void ParseDataFile( String fName )
		{
			try
			{
				XPathDocument xpd   = new XPathDocument( fName );								
				XPathNavigator xpn  = xpd.CreateNavigator();
				
				// framework
				XPathNodeIterator i = xpn.Select("//DATA/FWPath");
				if ( i.MoveNext() )	sFWPath = i.Current.Value;            
				
				// projpath
				i = xpn.Select("//DATA/ProjPath");
				if ( i.MoveNext() )	sProjPath = i.Current.Value;   
         
				// switch
				i = xpn.Select("//DATA/Switch");
				if ( i.MoveNext() )	sSwitch = i.Current.Value;   

				// source files
				i = xpn.Select("//DATA/SourceFile");
				while (i.MoveNext())
					sSourceFiles.Add ( i.Current.Value );
			}
			catch ( Exception ex )
			{
				throw new Exception( "Datafile parse error: " + ex.Message );
			}
		}

		static string CompileFiles()
		{
			string answer = "";
			try
			{
				int i;
				string arg = "";
				
				if ( sSwitch != "" )
					arg += " " + sSwitch; 

				for ( i=0; i<sSourceFiles.Count; i++ ) 
					arg += " \"" + sProjPath + @"\" + sSourceFiles[i] + "\"";

				Process p = new Process();

				p.StartInfo.UseShellExecute = false;
				p.StartInfo.RedirectStandardOutput = true;
				p.StartInfo.FileName  = sFWPath + @"\\csc.exe";
				p.StartInfo.Arguments = arg;
				p.Start(); 
				
				answer = p.StandardOutput.ReadToEnd();
				p.WaitForExit(); 
				
				p.Dispose(); 			
				
				return answer;
			}
			catch ( Exception ex )
			{
				throw new Exception( "Runtime exception: " + ex.Message );
			}
		}
	}
}

Example project file, edProject.XML:

<DATA>
<FWPath>c:\WINNT\Microsoft.NET\Framework\v1.0.3705</FWPath>
<ProjPath>D:\000</ProjPath>
<Switch>/nologo /target:exe</Switch>
<SourceFile>vic1.cs</SourceFile>
<SourceFile>vic2.cs</SourceFile>
</DATA>


Useful C# links: