1. 1
(6) Introduction of C# Basics – Advanced Features
– Part I
Nico Ludwig (@ersatzteilchen)
2. 2
TOC
● (6) Introduction of C# – Advanced Features – Part I
– Namespaces
– Assemblies
– Error Handling with Exceptions
– Enums
– Value Types and Reference Types
3. 3
Namespaces
● .Net namespaces allow to organize types generally.
– .Net's core types are defined in the namespace System.
● A namespace reduces the risk of clashing type names.
– Type names can be qualified with the namespace name.
– E.g. the types in libraries are held in own library namespaces.
● Syntactical specialties:
– Nesting: a namespace can contain other nested namespaces (like boxes in boxes).
– A namespace directive imports all types of a namespace with the using keyword.
– Only complete namespaces can be imported.
– Complex namespace names (e.g. nested ones) can be aliased.
– The . and :: punctuators can be used to access aliased namespaces.
– The global:: keyword allows to re-refer the global namespace (used rarely).
● What does that mean: types can "clash"?
● The CLI doesn't know the concept of namespaces, instead
it knows the concept of "DottedNames" and all type names
must be fully qualified in the IL code, though.
● C# also allows defining nested types as members, i.e.
type definitions in enclosing type definitions (e.g. classes
in classes). The syntax to access nested types is similar to
the syntax accessing nested namespaces (only the dot
punctuator is allowed, e.g.: OuterType.InnerType).
● The global:: keyword is often used, when code is
automatically generated by tools.
● When is such code getting generated?
● Attention: namespaces are open! - So we can spread our
namespace among several files, which is ok so far. But we
should not “extend” namespaces of the .Net framework
(like System) or namespaces from 3rd party vendors! The
types we develop at Avid should be held in a namespace,
being named following the coding conventions.
4. 4
Namespaces in C# Code
using System; // A namespace directive.
using SIO = System.IO; // Namespace System.IO aliased as SIO.
namespace Namespaces { // A namespace definition.
namespace OtherNamespace { // A nested namespace definition.
public class Program{}
}
public class Program {
public static void Main(string[] args) {
System.IO.File file = null; // A fully qualified type.
SIO.File file2 = null; // A via-alias qualified type.
SIO::File file3 = null; // A via-alias qualified type.
}
}
}
● Here namespaces are used to make two equally
names not clash. - This downright a bad design.
The idea of namespaces is not to resolve name
conflicts, but to organize type to make them
better searchable.
5. 5
Assemblies
● Assemblies are the smallest units of security and deployment.
– They allow modular development and separation of common code.
● Assemblies are self contained.
● Assemblies are represented by standalone executables or dlls.
● Types within assemblies can be encapsulated with the access modifier internal.
● The types defined in a namespace can be spread among assemblies.
– Often a certain assembly defines its types in its own namespace as a library.
– But this is not obligatory.
● Types that are declared as internal are
generally not accessible from other assemblies.
6. 6
Referencing 3rd Party Libraries into an Application
● We can reference libraries by referencing assemblies into the application:
● After the reference was added, a contained API can be accessed via its namespace.
// Use the namespace as qualifier:
System.Windows.Forms.Form f = new System.Windows.Forms.Form();
// Open the namespace with a namespace directive:
using System.Windows.Forms;
● This example shows how namespaces and
assemblies relate.
7. 7
Classical Handling of Run Time Errors
● Syntax errors vs. logic errors vs. run time errors.
● Classically, after an operation was done, the result should be checked.
– E.g. the content of a register is checked (in an assembly language).
– E.g. the returned value of a function is checked.
● If the result indicates error, the code has to branch...
● If an error condition was ignored (not checked), unexpected things may happen.
– This can lead to undefined behavior of an application.
● Dilemma: Effective code and error handling code is mixed.
● Syntax errors are errors that are found by the
compiler (e.g. typos in symbol names or
keywords).
● Logic errors are errors in the code that are not
found by the compiler but result in wrong behavior
of our code.
● Run time errors are a kind of logic errors that
result in an irrecoverable state of the program.
8. 8
Classical Handling of Run Time Errors in C Code
char* chars = (char*)malloc(10);
// Was memory allocation successful?
if (chars)
{
FillBuffer(chars);
printf("Buffer content: %s", chars);
free(chars);
}
else
{ // Oh, memory allocation was not successful!
printf("Ouch! Could not create char array!");
}
9. 9
Exception Handling in C# Code
try // Run time error prone code goes here:
{
string theText = File.ReadAllText("Text.txt"); // Might throw an Exception.
string a2ndText = File.ReadAllText("Text.txt"); // Might throw an Exception.
}
catch(FileNotFoundException exc) // We can handle FileNotFoundExceptions:
{
Console.WriteLine("FileNotFoundException thrown: "+exc.Message);
}
catch(Exception exc) // And we can handle other .Net Exceptions:
{
Console.WriteLine("Exception thrown: "+exc.Message);
}
finally // But under all circumstances this code will be executed:
{
Console.WriteLine("The last word is mine!");
}
● However, if an Exception is thrown in the first line,
the second line will not be reached at all.
● Exceptions in comparison to return statements:
Sometimes, if a very significant thing happens,
we want to pass control through multiple
functions. - Then a return statement is not
enough and Exceptions come into play.
● Error situations in ctors and many operators must
be done with exceptions, because we don't have
high flexibility on return types, however.
● In VB the possibilities with Catch clauses are
more elaborate:
● An optional Exit Try can exit from a Try or Catch
block, resuming the execution after the End Try
block.
● An optional When clause, being specified after
a Catch clause, can contribute additional
criteria that must meet together with the Catch's
exception type, so that the handler will execute.
10. 10
Run Time Error Handling with Exceptions
● Exception code separates effective code from error handling code syntactically.
– This is done by try and catch/finally blocks.
● Exception objects represent error conditions, they can't be ignored, but just...
– … "happen" (are being thrown) somewhere in the try block. And then:
● Alternative 1: Caught and handled in the matching catch block.
● Alternative 2: Ignored! - The method will be exited immediately and the exception will be forwarded.
– The stack of the exception will be unwound.
● Alternative 3: Suppressed, results in a postcondition as nothing has happened.
● Exceptions are types that are designed in an inheritance hierarchy.
● catch handlers work like "filters" for exceptions.
– The dynamic type of an Exception is matched against handler signatures.
– Hierarchical Exceptions and dynamic type checking works very intuitively.
● The term Exception has nothing to do with
frequency they "happen".
● Exceptions may be thrown everywhere and
every times, e.g. StackOverflowException,
OutOfMemoryException or
ExecutionEngineException.
11. 11
Enums
● Typisized set of integral constants.
– Useful for discrete integral constants or flags.
– Known from C/C++, Pascal and Java.
● Used to express options and flags in various .Net APIs.
– System.Windows.Forms.DialogResult
– System.IO.FileAttributes
– System.Text.RegularExpressions.RegexOptions
● An enum type implicitly inherits from the value type System.Enum.
– User defined enums can not define methods.
– But all enums provide some operator overloads, though.
● What does that mean "typisized set of integral
constants"?
● Constants that express instances of the same
concept. E.g. the four directions would be
represented with the constants North, East,
South and West. Because we'll only use these
constants' names in the code, we'll be never
interested in their "real" integral values. A
suitable name of that "integral set" type would
be "Direction".
● What are flags?
● The idea is that a single value can represent a
combination of values (so called flags).
12. 12
public void Foo()
{
// Reference type System.String:
String aString = "ABC";
// Assignment copies the reference, but both
// references point to the same object in heap.
String sndString = aString;
}
Memory Situation of Reference Types
Heap
Stack
aString = 0x23452113
Stack
sndString = 0x23452113
"ABC"
● A variable of a reference type represents a
pointer to an object on the heap. The variable is
only a shortcut to the "real" object in the heap.
13. 13
Value Types – Part I
● Reference types: All the types we have created by now are reference types.
– References, i.e. variables of reference type, hold only a shortcut to a location in heap.
– When the references are exchanged between methods, the objects won't be copied.
● This is call by reference: We're only passing around shortcuts to the same object.
– References can also refer to nothing, then they have the value null.
● But sometimes "lightweight types" are sufficient to represent a concept.
– Such as int, Point, KeyValuePair<K, V>, enum etc.
● These lightweight types are called value types in .Net, variables are called values.
● C# allows specifying user defined value types, so called structs.
● Value types are further divided into simple types,
enum types, struct types, and nullable types.
● Reference types are further divided into class
types, interface types, array types, and delegate
types.
14. 14
Value Types – Part II
● Local values differ from references:
– Values are created on the stack. They are default initialized!
– Reference types provide overhead, which value types do not need.
– They have different equality semantics by default (identity versus content equality).
● Value types in C#:
– Syntactically defined like classes, but with the keyword struct.
– As locals, they're created on the stack, even if the keyword new is used!
– Can't act as subtype or base type (structs are always sealed).
– Derive from type System.ValueType implicitly and can implement interfaces.
– Have a sequential memory layout (default) and act as backdoor to Win32 PODs.
● In C++ we can decide where (stack or heap) an object is created during creation.
– In C# we have to specify the "where" on the type definition (class or struct)!
● According local value objects: What is the "stack"?
● Reference types' overhead: they do carry a vtable cataloging
the virtual methods' addresses and a sync block index needed
for synchronization in multithreaded scenarios.
● Equality semantics: We can consider Reference types as
"entity types" that have "identity continuation", whereas for
value types we're not interested whether the value "green" is
the same "green" as tomorrow.
● Concerning value types can't act as a base type, there is one
exception: actually the type System.Enum derives from
System.ValueType and System.Enum is the base type of all
enum types.
● According value types in C#: What is a memory layout?
● It defines how the fields of a type are laid out in memory for
an instance: sequence, alignment and padding. By default,
structs are laid out sequentially (compatible with Win32
types) and classes are laid out automatically (the layout is
automatically optimized for the type by the runtime).
● What is a "POD"? Plain Old Datatype
15. 15
public void Bar()
{
// Value type System.Drawing.Point:
Point aPoint = new Point(1, 2);
// Assignment copies the value. Both objects
// have independent copies of the value
// {1, 2}. Modification modifies just one copy!
Point sndPoint = aPoint;
sndPoint.Y = 4;
}
Memory Situation of Value Types
Heap (is uninvolved)
Stack
aPoint = {X = 1, Y = 2}
Stack
aPoint = {X = 1, Y = 2}
sndPoint = {X = 1, Y = 4}
● A variable of a value type directly represents all
the values of the fields of the object on the stack.
16. 16
When to use Value Types
● Make the creation of user defined value types an exceptional case!
● When to use value types:
– Use them for small types. .Net enums are a good example for value types.
– Use them to interact with legacy (Win32), i.e. exploit the sequential memory layout.
– Exploit default content equality semantics (i.e. "value type" equality semantics).
● Contra value types:
– Don't use them if objects are often passed/returned to/from methods (call by value).
– Conversion from and to reference types should be avoided (boxing/unboxing).
– Value types can not be used in inheritance hierarchies.
– Value types can not be thread locked.
● C#'s structs are a completely different concept compared to C++' structs.
● In C++, classes vs. structs are means to control
accessibility of contained and inherited members.
In C#, classes vs. structs are used to define
different sorts of types, reference types and value
types.