Basic C# Syntax
Up Java Resources C++ Resources Comp410

C# follows the normal C/C++/Java conventions of method invocations, flow control and statements using the usual semicolons, parenthesis, and curly braces.   For full details, please see Microsoft's online C# reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/CSharpSpecStart.asp

Below, you will find summaries of some of the unique syntax features of C#:

Topics:


Accessibility

Accessibility is controlled with the following keywords:

  • public  -- Completely open for access 
  • protected  -- Access granted to enclosing class and subclasses
  • internal -- Access granted to classes in the enclosing assembly
  • protected internal -- Access granted to the enclosing assembly and sub-classes of the enclosing class.
  • private -- access granted to the enclosing class only.

The default accessibility level depends on where the entity is: e.g. top-level classes are public by default, but class members are private by default.   Do not rely on default accessibility levels.

top

Declaring classes

The syntax for declaring a class is essentially the same as C++:

[accessiblilty] class MySubClass : MySuperClass {
    
...
}

All classes implicitly derive from the class object.

top

Abstract Methods and Classes

Classes can be declared abstract to model abstract entities and thus prevent intantiation.

Likewise, abstract methods can be declared with no code bodies.

A class declared sealed cannot be sub-classed.   Methods can also be sealed.    Equivalent to the Java  final.

The keyword base refers to the immediate superclass -- equivalent to Java's super.

top

Overriding methods

Unlike Java or C++, C# does not automatically override a method in a superclass if a subclass defines a method with the same signature.

In order to be overriden by a derived class, a method must be declared either abstract or virtual.   A virtual method differs from an abstract method in that it has a code body which gives it default behavior.

In addition, the method that is overriding a base class's method must be declared with the override keyword.

Consider the following code snippet:

      public class MySuperClass {
		public string getName() {
			return "SuperClass";
		}
		public virtual getVirtualName(){
			return "Virtual SuperClass";
		}
      }
      
      public class MySubClass : MySuperClass {
		public string getName() {
			return "SubClass";
		}
		public virtual getVirtualName(){
			return "Virtual SubClass";
		}
      }
      
      
      ...
	  MySuperClass superC = new MySubClass();
      
      string s_non_virtual = superC.getName();
      string s_virtual = superC.getVirtualName();
		      
      

The behavior is similar to C++ in that s_non_virtual will refer to the string "SuperClass" because the not declaring the method virtual generates superclass behavior when a variable is typed as the superclass.   This is markedly different behavior than Java which always returns the behavior of the instantiated type, not the declared type!

To get the subclass's behavior the the superclass's method must be declared virtual a plus the subclass's method must be declared as an override.   Thus s_virtual has the value we would expect in Java, namely it refers to the string "Virtual SubClass". 

Note that if MySubClass.getVirtualName had not been declared as an override, then s_virtual would have received at reference to the superclass's result, "Virtual SuperClass".

Most good OO code rarely desires the declared type's (superclass) behavior but rather it is the instantiated type's (subclass) behavior that is desired.    So most methods in top level classes will need to be declared virtual, though this does assess a performance penalty.

In addition, C# includes the new keyword for methods.   The new modifier hides a virtual base class method.  This forces superclass behavior when a subclass instance is referenced by a superclass variable.    For instance, if MySubClass.getVirtualName was declared new, then s_virtual would reference the string "Virtual SuperClass ". 

The new modifier also enables a class to hide a base class's method with the same signature when the base class method is not virtual.

top

Constructors

Constructors in C# works similarly to those in C++ and Java with a few differences.  Multiple constructors can be defined as usual.   To call a particular constructor of the base class, the following example's syntax is used:

public MySubClass(string s) : MySuperClass(s) {
    ....

Note that C# explicitly enforces the call to the base class constructor before the class constructor.

The this parameter cannot be used in the call to the base class constructor above because the object hasn't been fully constructed yet.

C# also provides a mechanism to programatically initialize static members of a class.   A static constructor can be defined to initialize the static members of a class as such:

static MyClass() {
  ...
}

Static constructors are implicitly public and necessarily take no input parameters.   Since classes are loaded dynamically, there is no guarantee of when the static constructor will be called, other than before any instances of the class are constructed.   The static constructor will be called only once.

top

Interfaces

Like Java, C# supports a single class inheritance model with multiple interface inheritance.

The syntax to declare a class's implementation of an interface is the same as declaring an inheritance from a base class except that multiple interfaces can be listed:

class MyClass : MyInterface1, MyInterface2, .... {
    ....
}

Unlike Java, the accessibility of interfaces is not restricted to public but includes the full range of accessibilities listed above. 

The new modifier identifies an interface that hides methods in one of interfaces it implements.

top

Namespaces

Namespaces provide a hierarchal encapsulation of classes.   Classes are declared as belonging to a particular namespace by writing:

namespace [name of namespace]
{

     [Code belonging to the above namespace goes here]

}

The namespace is part of the fully qualified name for any class that it contains, e.g. if MyClass is in MyNamespace then MyClass can always be referenced by MyNameSpace.MyClass.

To use classes in another namespace without writing the fully qualified class name, simply write:

using [namespace name];

Namespaces can contain namespaces, so a fully qualified class name may be something like MyNamespace.MySubSpace.MyClass.

C# namespaces differ from Java packages in a number of significant manners:

  • Namespaces are not tied to the directory structure.  It is recommended that they do match up however because it makes it easier to keep track of files.
  • Namespaces are truly heirarchal--if you use a particular namespace, then you can access all namespaces below that one with relative names.   That is if you have a class MyNamespace.MySubSpace.MyClass, then if you say "using MyNameSpace" then you refer to MyClasssimply by writing MySubSpace.MyClass.

top

Reference Passing

Ala C++, C# normally passes everything by value but can pass by reference if needed.  Unlike C++, both the callee (the method that uses the reference) as well as the caller need to specify that a reference is being used.

To define a method as taking a reference as an input, use the ref modifier with the desired input parameter:

void myMethod(ref int x) {...}

 The caller must also use the ref keyword as well when calling myMethod:

int z = 5;

aClass.myMethod(ref z);

Any assignments made to the x inside of myMethodwill affect z in the caller.

Note that a ref input parameter cannot be called with a null value.

One explicity specify that a side effect is intended by using the out modifier instead of the ref modifier above.    If out is used, the compiler will insist that the method assign a value to that parameter.

 

top

Variable number of parameters

A variable number of input parameters can be specified by using the params modifier:

double myFunc(double z, params int[] xs) {...}

The above method can be called with various numbers of integers:

myObj.myFunc(3.14, 4);

myObj.myFunc(2.7, 2, 5, 42);

The restrictions on using params are:

  • params must be applied only to the last input parameter in the parameter list.
  • The input variable used with params must be a one-dimensional array.
  • There can be only one params used in a method signature.

top