Ryan's Manuals

C#

MS thought C+++ would be a silly name for this Java clone.


Contents



using System;

class Hello
{
  static void Main()
  {
    Console.WriteLine("Hello, C#!");
  }
}

Why use C#?

Pronounced “C Sharp” and also known as Microsoft’s Java, C# is a general-purpose language primarily used for developing Windows applications. C# is also used by the Unity game engine, the ASP.NET framework (which is used by many of the highest-traffic websites,) and should remain relevant for years to come. I am personally learning C# for development on the Sitecore® platform, a web content management system built with Microsoft’s .NET system. Working with a large, technical system should provide excellent experience.

Setup

On GNU/Linux, you’ll need to install the mono tools. Mono is a libre implementation of Microsoft’s C#. To compile and run C# code, install the mono-devel package on your distro of choice. After installation, you’ll be able to invoke csc file.cs to compile your C# file, and mono file.exe to run it.

On Windows, you’ll need to find a C# compiler, like the one included in Visual Studio or “Roslyn”, the MS open-source C# compiler, and add the location of csc to your path. After csc has been added to the Windows PATH, csc file.cs can be run to compile your C# file, and .\file.exe will run it.

GNU/Linux compile+run script cs.sh:

if [ -z $1 ]; then
  echo "Please include a .cs file as the first argument."
else
  y="${1%.cs}"
  mcs -out:prog.exe $y.cs
  echo "Compiled $y.cs to prog.exe, running..."
  mono prog.exe
fi

Windows compile+run script cs.bat:

@ECHO OFF
ECHO **************************************************
ECHO **        FOR WIN10x64: C# Compile N' Run       **
ECHO **************************************************
ECHO Using %1

csc /out:prog.exe %1
.\prog.exe

Both of these scripts take one argument, a .cs program, then proceed to compile it using csc and run it. Having scripts to do little repetitive things like this saves a lot of time and is highly recommended.

A C# source file can be compiled into the following “assemblies”:

dotNET Core on GNU/Linux

Mono isn’t nearly as stable or featureful as Microsoft’s full implementation of C#. To use all the features of modern C#, it is necessary to install .NET Core. Microsoft has gone out of their Windows-Server-Only way to make their products and services compatible with ‘Linux’, and my experinece with dotNET Core has been positive. Creating new projects, building, running and packaging have all been smooth processes on Fedora 28.

After installing .NET Core and the .NET SDK, create a new project by invoking dotnet:

dotnet new console -o projectName
cd projectName
dotnet run
 #> Hello, World!

To run from the cs folder, execute:

dotnet run --project rcfxxx_folder

Get started with .NET Core.

C# Basics

Hello World

using System;

class Test
{
  static void Main()
  {
    int x = 12 * 30; //Integer X, local variable.
    Console.WriteLine(x); //Print x to the cli.
  }
}

Simple Functions

using System;

class Program
{
  static void Main() // Default entry point of execution.
  {
    Console.WriteLine("30 feet to inches: "+ FeetToInches(30));
    Console.WriteLine("13 feet to inches: "+ FeetToInches(13));
    Console.WriteLine("44 feet to inches: "+ FeetToInches(44));
  }

  static int FeetToInches(int feet)
  {
    int inches = feet * 12;
    return inches;
  }
}

Namespaces

using System;

namespace RCF_NSP01
{
  class RCF_CLS01
  { ... }

  class RCF_CLS02
  { ... }
}

More complex class/namespace calling.

using System;

namespace RCF_NSP01
{
  class RCF_CLS01
  // Classes can be loaded separately by other programs!
  // Ex. `using Namespace.Class;`
  {
    static void Main() // Only a single class can have the entry point.
    {
      Console.WriteLine("RCF003 - Namespaces.\n");
      Console.WriteLine("Class one loaded!");
	  Console.WriteLine( "16 -> " + Double(8) );
	  Console.WriteLine( "4  -> " + RCF_NSP01.RCF_CLS02.DivideByTwo(8) );
	  Console.WriteLine( "7  -> " + RCF_NSP01.RCF_CLS03.DivideByThree(21) );
    }
    static int Double(int x)
    {
      return x*2;
    }
  }
  class RCF_CLS02
  {
    public static int DivideByTwo(int x)
    {
      return x/2;
    }
  }
  class RCF_CLS03
  {
    public static int DivideByThree(int x)
    {
      return x/3;
    }
  }
}

C# Syntax

using System;

class Program
{
  static void Main()
  {
    int a = 123/4;
    Console.WriteLine(a);
  }
}

Types

int x = 144/12;
string message = "Hello World!";
const int life = 42;
bool george = true;

Types define what kind of value is stored in a location. Variables can be changed, but a constant will always represent the same value. C# provides predefined int, string, and bool types. Types are categorized like so:

Custom Types

Custom Types can be created from simpler types.

using System;

namespace TypeTest
{
	class Program
	{
	  static void Main()
	  {
		Multiplier m1 = new Multiplier (3);
		Console.WriteLine( m1.Apply(3) ); // Prints 9

		Multiplier m2 = new Multiplier (99);
		Console.WriteLine( m2.Apply(1) ); // Prints 99
	  }
	}

	public class Multiplier
	{
		// Data members:
		int factor; // Field

		// Function members:

		// Constructor
		public Multiplier ( int input ) { factor = input; }

		// Method
		public int Apply ( int input ) { return input * factor; }
	}

}

Data is made availabe to a program when it is instantiated. Built-in types can be instantiated by using a literal ( true , 18 ). Custom types use the new operator, which passes arguments to the constructor.

Kraken Steve = new Kraken("Steve Jones");

Data members and function members that don’t operate on the instance of the type, but rather on the type itself, must be marked as static.3

Therefore, static data members are shared among all instances of a custom type. This is demonstrated below:

StaticTests custom type.

public class StaticTests
	{
		public static int isStatic;
		public int notStatic;

		// 'public' keyword exposes method to other classes.
		public StaticTests ()
		{
			isStatic++;
			notStatic++;
			Console.WriteLine("Static: "+isStatic);
			Console.WriteLine("Not static: "+notStatic);
		}
	}

Main, each instantiation adds 1 to both isStatic and notStatic.

StaticTests a = new StaticTests();
StaticTests b = new StaticTests();
StaticTests c = new StaticTests();
StaticTests d = new StaticTests();
StaticTests e = new StaticTests();
// At this point, isstatic == 5.
// Not static for each instance is 1.

Conversions and Casting

int a = 134; //An integer (32 bits.)
long b = a; //Conversion (64 bits.)
short c = (short)a; //Cast (16 bits.) Possible data loss.

Number Types

Whoa! Conversions, overflow and many types… oh my! While this is great, I’m skipping it for now because it looks familiar to the way it’s implemented in Java. This example is interesting, though:

//From "C# 5.0 in a Nutshell" Page 26.
int x = 0, y = 0;
Console.WriteLine (x++); // Outputs 0; x is now 1
Console.WriteLine (++y); // Outputs 1; y is now 1

Boolean Types

Booleans in C# can be evaluated with the usual comparators, equal == and not equal !=, along with and &&, or || and brackets ( ). Using single & or | operators will cause all items to be evaluated instead of “short-circuiting” and breaking as soon as the evaluation can only be true.

C# also has a ternary operator that takes three inputs: A boolean statement, the action for true, and the action for false:

return (a > b) ? a : b;

String Types

Arrays

An array is a container for variables, and can hold an explicit number of values of a given type. Simple arrays are initialized like type[] array = new type[length];. For example, an array of 12 characters would be instantiated as char[] arr = new char[12];. (As per CompSci norms, an array with 12 characters will be addressable as indices zero through eleven, for a total of 12 indices.) Simple creation and reading of an array is shown below:

using System;

class Program
{
  static void Main()
  {
    Console.WriteLine("RCF007 - Arrays\n");

	//Simple declaration:
	int[] letters = new int[5];

	//Iterate through and assign values, .length returns length.
	for(int i=0; i < letters.Length; i++)
	{
	  letters[i] = 10-i;
	}

	//Print values
	for(int i=0; i<letters.Length; i++)
	{
	  Console.WriteLine("letters["+i+"] = "+letters[i]);
	}

	PrintArray(new int[] {1, 2, 3, 234, 32, 2, 1});
  }


  static void PrintArray (int[] array)
  {
	Console.WriteLine("Contents of Array:");
	for(int i=0; i<array.Length; i++)
	{
	  Console.WriteLine("array["+i+"] = "+array[i]);
	}
  }

}

An array will always be initialized with the default values for the structure in question. If the array is filled with a type, it will be filled with the default values for that type.

int[] x = new int[] {1, 2, 3, 234, 32, 2, 1};

It is possible to initialize and populate an array in one line:

int[] x = new int[] {1, 2, 3, 234, 32, 2, 1};

Square Arrays

//Simple declaration:
int[] Letters = new int[5];

//SQUARE arrays:
//Declare a matrix:
int[,] Matrix = new int[3,3];

//Declare and fill a matrix:
int[,] MatrixTwo = new int[,]
{
  {0,1,2},
  {3,4,5},
  {6,7,8}
};

Jagged Arrays

//Jagged array with outermost dimension 3:
int[][] JaggedOne = new int[3][];
//(Internal arrays are null.)

//Jagged array with alternate instantiation:
int[][] JaggedTwo = new int[][]
{
  new int[]{1,2,3},
  new int[]{4,5,6,7,8},
  new int[]{9,10,11}
};

//Initialize internal arrays:
for (int x=0; x<JaggedOne.Length; x++)
{
  JaggedOne[x] = new int[3];
  for (int y=0; y<JaggedOne[x].Length; y++)
  {
    JaggedOne[x][y]= x*3+y;
  }
}

Variables and Parameters

C# enforces definite assignment, where it is impossible to access memory that has not been initialized by the running program. Calling an unassigned variable will usually result in a compile-time error unless the assignment method infers a default value.

Both of these structures have garbage collection active, freeing memory by deallocating any variables, parameters or objects that are no longer referenced by living code.

TypeDefault Value
Reference typesnull
Numeric types0
CHARs'\0'
BOOLsfalse

To pass by reference, ref can be used in a function definition (static void Foo(ref int x)) instead of the default which copies. This is essential for some methods that must alter the input data.

//From Page 43 of C# In a Nutshell:
class Test
{
  static void Swap (ref string a, ref string b)
  {
    string temp = a;
    a = b;
    b = temp;
  }
  static void Main()
  {
    string x = "Penn";
    string y = "Teller";
    Swap (ref x, ref y);
    Console.WriteLine (x); // Teller
    Console.WriteLine (y); // Penn
  }
}

Note to self: return to this section to review the other methods to pass by reference, out and params.

Statements

Varibles are delcared, and the scope extends within the encapsulating block.

C# is able to initialize variables if the compiler is able to infer the type by the initialization expression:

var x = "String"; //String
var y = new System.Text.StringBuilder(); //System.Text.StringBuilder
var z = 1.21; //float

Expressions can be of a few forms:

Assignment | x = 2 + 2; Increment | x++; Assignment | x = Math.Max(a,b,c); Method Call | Console.Writeline(y); Assignment | var sb = new Klaxon(); Object Instantiation | new Klaxon();

Selection statements, or control flow, include if, else, and switch.

if else

if ( boolean )
{
  Console.WriteLine("Boolean was true.");
} else if ( anotherBoolean ) {
  Console.WriteLine("Another boolean was false.");
} else {
  Console.WriteLine("Both booleans were false.");
}

switch

switch ( number )
{
  case 1:
    Console.WriteLine("Option I");
    break;
  case 2:
  case 3:
  case 4:
    Console.WriteLine("Option II-IV");
    break;
  case 5:
    Console.WriteLine("Option V");
    break;
  default:
    Console.WriteLine("Out of bounds option.");
    break;
}

Iteration statements include while, do-while, for, and foreach.

while while ( boolean )

while ( x < 1000 )
{
  Console.WriteLine(x);
  x++;
}

do-while tests the expression after execution.

do
{
  Console.WriteLine(x);
  x++;
}
while ( x < 1000 );

for has clauses: for ( initialization ; condition ; iteration )

for ( int x=0, y=1000; x < y; x++ )
{
  Console.WriteLine( x + ", " + y );
}

…any of the clauses can be omitted. for (;;) is valid as an infinite loop.

foreach iterates over enumerable lists of elements.

for ( char c in "chopsticks" )
{
  Console.WriteLine( c );
}

Jump statements include break, continue, goto, return and throw.

break ends the execution of an iteration or switch.

while ( true )
{
  if ( x > y )
    break;
}

continue moves to the next iteration of a loop.

for ( int x=0, y=1000; x < y; x++ )
{
  if ( ( x%2 ) == 0 )
    continue; //Skips printing for even numbers.

  Console.WriteLine( x + ", " + y );
}

goto moves execution to a statement-label.

int x = 4;
labelA:
if( x < 10)
{
  x++;
  goto labelA;
}

return exits the method and returns a method corresponding to the method’s return type.

return true;

throw is used to indicate errors.

if ( x == null )
  throw new ArgumentNullException();

Addressing Types in Namespaces

namespace defines the namespace for types within a block.

namespace Outer.Middle.Inner
{
  class A {}
  class B {} //Outer.Middle.Inner.B
}

Class B has a fully qualified name of Outer.Middle.Inner.B

using imports a namespace. Importing the Outer.Middle.Inner shown above allows for the use of the A and B type without using the fully qualified name.

using Outer.Middle.Inner;

A objA = new A();
B objB;

Namespace rules:

  1. Type names can be re-used within a namespace, but not at the same level.
  2. Names declared in the outer namespaces can be used unconditionally in inner namespace.
  3. Namespace declaraions can exist in multiple files.
  4. To import a single type or partial namespace, use an alias:
using RLogger = System.Custom.Logs.RLogger;

Creating Types

Definitions:

Fields allow the following field modifiers:

static | Static something public | Allows external method access. internal | private | protected | new | unsafe | readonly | Prevents modification after construction. volatile |

See this MS doc on Access Modifiers for more information.

Monodevelop

When first installing monodevelop, it was very unstable. I ran it from the command line to see any errors, and sure enough:

Error in `monodevelop': free(): invalid pointer: 0x00007f629c001a80
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x70bfb)[0x7f630072cbfb]
/lib/x86_64-linux-gnu/libc.so.6(+0x76fc6)[0x7f6300732fc6]

Running apt-get build-dep monodevelop fixed all of my stability problems.

References

Many are from “C# 5.0 in a Nutshell” 5e. J. and B. Albahari, O’Reilly 2012.


  1. “C# 5.0 in a Nutshell” 5e. Page 106. Generics. ↩︎

  2. “C# 5.0 in a Nutshell” 5e. Page 177. Unsafe Code and Pointers. ↩︎

  3. “C# 5.0 in a Nutshell” 5e. Page 17. Type Basics. ↩︎



Site Directory



Page Information

Title: C#
Word Count: 2567 words
Reading Time: 13 minutes
Permalink:
https://manuals.ryanfleck.ca/cs/