Reading 2: Basic C#
Credits: This reading is a port from Java to C# from 6.005
— Software Construction on MIT OpenCourseWare . Click here
to see the original reading based on Java.
Objectives
-
Learn basic C# syntax and semantics
-
Transition from writing Python to writing C#
Recall the focus of this course!
Safe from bugs |
Easy to understand |
Ready for change |
---|---|---|
Correct today and correct in the unknown future. |
Communicating clearly with future programmers, including future you. |
Designed to accommodate change without rewriting. |
Getting started with C# Tutorials
The next few sections link to the C# Tutorials to get you up to speed with the basics.
You may also find this C# tutorial helpful as an alternative resource.
This reading and other resources will frequently refer you to the .NET API documentation which describes all the classes built in to C#. The .NET Framework provides lots of features which can be used with a variety of languages – not just C#. For example, the .NET framework defines in its System module a class called Console which have a method WriteLine that outputs text to the console. We’ve already seen Console in our HelloWorld program!
Language basics
Read C# Program Structure and C# Basic Syntax. Try running these programs yourself. For example, create a file hello.cs that contains your C# program. At the command line, compile and run as follows:
$ csc hello.cs
$ mono hello.exe
Types and variables
Read about Types, Type conversions, Variables, Constants, Arrays, and Strings.
Operators, expressions, and control flow
Read about Operators, Decision Making, and Loops.
Classes and objects
Read about Encapsulation, Methods, Classes, Inheritance, and Polymorphism.
Snapshot diagrams
It will be useful for us to draw pictures of what’s happening at runtime, in order to understand subtle questions. Snapshot diagrams represent the internal state of a program at runtime – its stack (methods in progress and their local variables) and its heap (objects that currently exist).
Snapshot diagrams are similar to function stack diagrams in your previous Swarthmore courses.
Recall why function/snapshot diagrams help us:
To talk to each other through pictures (in class and in team meetings)
To illustrate concepts like primitive types vs. object types, immutable values vs. immutable references, pointer aliasing, stack vs. heap, abstractions vs. concrete representations.
To help explain your design for your team project (with each other and with your TA).
To pave the way for richer design notations in subsequent courses. For example, snapshot diagrams generalize into object models in 6.170.
Although the diagrams in this course use examples from C#, the notation can be applied to any modern programming language, e.g., Python, Java, JavaScript, C++, Ruby.
Primitive values
Primitive values are represented by bare constants. The incoming arrow is a reference to the value from a variable or an object field.
Object values
An object value is a circle labeled by its type. When we want to
show more detail, we write field names inside it, with arrows
pointing out to their values. For still more detail, the fields can
include their declared types. Some people prefer to write x:int
instead of int x
, but both are
fine.
Mutating values vs. reassigning variables
Snapshot diagrams give us a way to visualize the distinction between changing a variable and changing a value:
When you assign to a variable or a field, you’re changing where the variable’s arrow points. You can point it to a different value.
When you assign to the contents of a mutable value – such as an array or list – you’re changing references inside that value.
Reassignment and immutable values
For example, if we have a s
string
variable s
, we can reassign
it from a value of "a"
to "ab"
.
String s = "a";
s = s + "b";
String
is an example of an immutable
type, a type whose values can never change once they have been
created. Immutability (immunity from change) is a major design
principle in this course, and we’ll talk much more about it in
future readings.
Immutable objects (intended by their designer to always represent
the same value) are denoted in a snapshot diagram by a double border,
like the String
objects in our diagram.
Mutable values
By contrast, StringBuilder
(another built-in C# class) is a mutable object
that represents a string of characters, and it has methods that
change the value of the object:
StringBuilder sb = new StringBuilder("a");
sb.append("b");
These two snapshot diagrams look very different, which is good: the difference between mutability and immutability will play an important role in making our code safe from bugs .
C# Collections
The very first Language Basics tutorial discussed arrays , which are fixed-length containers for a sequence of objects or primitive values. C# provides a number of more powerful and flexible tools for managing collections of objects.
Lists, Sets, and Dictionaries
A C#
List
is similar to a Python
list . A List
contains an
ordered collection of zero or more objects, where the same object
might appear multiple times. We can add and remove items to and from
the List
, which will grow and shrink to
accomodate its contents.
Example List
operations:
C# |
description |
Python |
---|---|---|
|
count the number of elements |
|
|
append an element to the end |
|
|
Access first element |
|
In a snapshot diagram, we represent a List
as an object with indices drawn as fields:
This list of cities
might represent a
trip from Boston to Bogotá to Barcelona.
A HashSet
is an unordered collection of zero or more unique
objects. Like a mathematical set or a Python
set – and unlike a List
– an
object cannot appear in a set multiple times. Either it’s in or
it’s out.
Example Set
operations:
C# |
description |
Python |
---|---|---|
|
test if the set contains an element |
|
|
test whether s1 ⊇ s2 |
|
|
remove s2 from s1 |
|
In a snapshot diagram, we represent a HashS
et
as an object with no-name fields:
Here we have a set of integers, in no particular order: 42, 1024, and -7.
A Dictionary
is similar to a Python
dictionary .
Example Dictionary
operations:
C# |
description |
Python |
---|---|---|
|
add the mapping key → val |
|
|
get the value for a key |
|
|
test whether the map has a key |
|
|
delete a mapping |
|
In a snapshot diagram, we represent a Dictionary
as an object that contains key/value pairs:
Literals
Python provides convenient syntax for creating lists:
lst = [ "a", "b", "c" ]
And maps:
map = { "apple": 5, "banana": 7 }
C# provides a literal syntax for arrays:
s
tring[] arr = { "a", "b", "c" };
But this creates an array , not a List
However, there is similar syntax for Lists and Dictionaries:
List<string> list = new List<string>(){ "a", "b", "c" };
Dictionary<string,int> dict = new Dictionary<string,int>(){ {"a",0}, {"b",1}, {"c",2} };
Declaring List, HashSet, and Dictionary variables
Unlike Python collection types, with C# collections we can restrict the type of objects contained in the collection. When we add an item, the compiler can perform static checking to ensure we only add items of the appropriate type. Then, when we pull out an item, we are guaranteed that its type will be what we expect.
Here’s the syntax for declaring some variables to hold collections:
List<
s
tring> cities; // a List of Strings
Hash
Set<
int
> numbers; // a Set of Integers
Dictionary
<
s
tring,Turtle> turtles; // a Map with String keys and Turtle values
Iteration
So maybe we have:
List<
s
tring> cities = new List<
int
>();
Hash
Set<
i
nt> numbers = new HashSet<
int
>();
Dictionary
<
s
tring,Turtle> turtles = new
Dictionary
<
string,Turtle
>();
A very common task is iterating through our cities/numbers/turtles/etc.
In Python:
for city in cities:
print city
for num in numbers:
print num
for key in turtles:
print "%s: %s" % (key, turtles[key])
C# provides a similar syntax for iterating over the items
Here’s the C#:
for
each
(
s
tring city
in
cities) {
Console.WriteLine
(city);
}
for
each
(int num
in
numbers) {
Console.WriteLine
(num);
}
for
each
(
KeyValuePair<string,Turtle>
entry in turtles
) {
Console.WriteLine(entry.Key
+ ": " +
entry.Value
);
}
Under the hood this kind of for
loop
uses an Iterator
, a design pattern we’ll see later in the class.
Iterating with indices
If you want to, C# provides different for
loops that we can use to iterate through a list using its
indices:
for (int ii = 0; ii < cities.size(); ii++) {
Console.WriteLine
(cities
[
ii
]
);
}
Unless we actually need the index value ii
,
this code is verbose and has more places for bugs to hide. Avoid.
C# API documentation
The previous section has a number of links to documentation for classes that are part of the C# .NET API.
API stands for application programming interface . If you want to program an app that talks to Facebook, Facebook publishes an API (more than one, in fact, for different languages and frameworks) you can program against. The C# API is a large set of generally useful tools for programming pretty much anything.
System.string
is
the full name of a C# string. We can
create objects of type String
just by
using "double quotes"
.
System.Collections.Generic.List
is like a Python list, but in Python, lists are part of
the language. In C#, List
s are
implemented in… C#!
Collections.Generic.Dictionary
is like a Python dictionary.
System.IO.File
represents a file on disk. Take a look at the methods
provided by File
: just like with
Python files, we read in all the lines of a file or process them one
by one.
Let’s take a closer look at the documentation for File
. There are many things here that relate to features of C#
we haven’t discussed! Keep your head and focus on the things
in bold below.
At the top of the page is the namespace for File
.
Namespaces are containers that help organize and separate
code. In particular, they help us avoid naming conflicts between
modules. If another system also defines a class called File,
it’s ok. The namespace will help us keep track of which File
we are using!
We also see a brief description, its declaration, followed by its
inheritanance. File
is a subclass of Object.
A File
object
has all of the methods of Object
(plus its own methods) available to use. Any subclasses would also
be shown here but File
doesn’t have any.
Next, we see an example showing how to use File. Below that we have a list of all available methods.
Below the summary are detailed descriptions of each method and constructor. Click a constructor or method to see the detailed description. This is the first place you should go to understand what a method does.
Each detailed description includes:
The method signature : we see the return type, the method name, and the parameters. We also see exceptions . For now, those usually mean errors the method can run into.
The full description .
Parameters : descriptions of the method arguments.
And a description of what the method returns .
Specifications
These detailed descriptions are specifications .
They allow us to use tools like st
ring
, Dictionary
, or File
without having to read or understand the code that
implements them.
Reading, writing, understanding, and analyzing specifications will be one of our first major undertakings in CS71, starting in a few classes.