C# Fundamentals

dotnet CLI

  • Create new dotnet project
1
2
dotnet new console
dotnet new xunit
  • Add unget package
1
dotnet add package xunit --version 2.4.1
  • Add reference to another project in .csproj file
1
dotnet add reference projectName.csproj
  • Run unit test project
1
dotnet test
  • Run main project
1
dotnet run
  • Create solution file to include all projects
1
dotnet new sln
  • Add project into the solution file
1
dotnet sln add PathToProjectFile.csproj
  • Build all project in solution
1
dotnet build
  • C# passes variables by value unless you use keyword e.g. ref, out

  • C# has garbage collector

Auto property

1
2
3
4
5
6
7
8
public string Name {
get {
return name;
}
set {
name = value
}
}
  • This code can be simplified to
1
public string Name { get; set; }
  • difference between auto property and field
1
2
3
public string Name {get; set;}

public string Name;
  • You can specify custom code in getter and setter functions.
  • When serialize objects, fields may not be counted.

readonly

1
readonly public string category;
  • readonly can only be assigned in constructor.

const

1
const public string CATEGORY
  • const is more strict than readonly, you can’t assign it in constructor, it is not a variable. Once you initialized it, you can’t change its value. And when you access it. You can access it from the class name, not the object name. Because every object instance will have the same value for this const. So its better to access it from the class. Its more clear.
1
2
3
4
5
6
7
// Book is a class
// CATEGORY is a const in Book
// Category is a readonly field in Book
Console.WriteLine(Book.CATEGORY); // const

var book1 = new Book();
Console.WriteLine(book1.Category); // readonly field

Delegate

  • A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public delegate string WriteLogDelegate (string logMessage);

// reference the main project to the test project in .csproj
public class TypeTests
{
[Fact]
public void WriteLogDelegateCanPointToMethod() {
WriteLogDelegate log = new WriteLogDelegate(ReturnMessage);

var result = log("Hello!");
Assert.Equal("Hello!", result);
}

private string ReturnMessage(string message) {
return message;
}
}

Multi-Cast Delegate

  • One delegate variable can points to multiple methods, and by invoking that delegate variable, all subscribed methods will be invoked too
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public delegate string WriteLogDelegate (string logMessage);

// reference the main project to the test project in .csproj
public class TypeTests
{
int count = 0;

[Fact]
public void WriteLogDelegateCanPointToMethod() {
WriteLogDelegate log = new WriteLogDelegate(ReturnMessage);
log += IncrementCounter;

var result = log("Hello!");
Assert.Equal(2, count);
}

private string ReturnMessage(string message) {
count++;
return message;
}

private string IncrementCounter(string message) {
count++;
return message.Tolower();
}
}

Event

  • Sometimes after we have done someting, we want to broadcast it to all people that are interested. The broadcast delegate usually takes two parameters, the object sender and EventArgs. object is the base type in C#, all types and classes can be fitted into object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public delegate void GradeAddedDelegate(object sender, EventArgs eventArgs);

public class Book {
public event GradeAddedDelegate GradeAdded;

public void AddGrade(double grade) {
if (grade <= 100 && grade >= 0) {
grades.Add(grade);

// broadcast
// if GradeAdded is null, then no one is listening to this event
// so there is no need to invoke the delegate.
if (GradeAdded != null) {
GradeAdded(this, new EventArgs())
}

} else {
throw new ArgumentException($"Invalid {nameof(grade)}");
}
}
}
  • In program.cs where we are trying to use this Book object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
{
static void Main(string[] args)
{
var book = new Book("Yuan's Grade Book");

// add OnGradeAdded to this delegate
book.GradeAdded += OnGradeAdded;
}

static void OnGradeAdded(object sender, EventArgs e) {
Console.WriteLine($"A grade was added.");
}
}

OOP

Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class NamedObject {
// Name field
public string Name {get; set;}

// constructor
public NamedObject(string name) {
Name = name;
}
}

// inherit from NamedObject base class
public class BookBase : NamedObject {
public BookBase(string name) : base(name) {
Name = name;
}
}

public Program clas {
static void Main(string[] args)
{
// when you create a new instance of Book
// you assign the new name Book 1 to Book class
// Book class will pass the same name to its base class
Book book1 = new Book("Book 1");
}
}
  • When you want to use some attributes or functions from a base class, instead of rewrite the code, you can inherit the base class. The constructor of the child class will also need to pass in the variable to its base class

Polymophism

1
2
3
4
5
public abstract class BookBase : NamedObject {
public BookBase (string name) : base(name) {}

public abstract void AddGrade(double grade);
}
  • The abstract modifier indicates that the thing being modified has a missing or incomplete implementation. And it will be implemented by its child class. Child class who inherit the base class will need to add an override keyword to implement the abstract function

  • Base class can have different implementations of it. A Car base class may have Truck and Sport Car as its children class and they have some same base field and functions but also many different.

Interface

  • Where abstract may content actual implementation of some field and functions (some is missing). The Interface class doesn’t have any implementations.
1
2
3
4
5
6
7
8
9
namespace GradeBook
{
public interface IBook
{
void AddGrade(double grade);
string Name {get;}
event GradeAddedDelegate GradeAdded;
}
}

Virtual

  • For base class, there may be some functions that already have an implementation. But Virtual keyword meaning its children class may choose to override it.

Difference between Virtual and Abstract

  • Virtual methods have an implementation and provide the derived classes with the option of overriding it. Abstract methods do not provide an implementation and force the derived classes to override the method.

  • So, abstract methods have no actual code in them, and subclasses HAVE TO override the method. Virtual methods can have code, which is usually a default implementation of something, and any subclasses CAN override the method using the override modifier and provide a custom implementation.

using

  • When we are writing text to file, we need to open the file and close it after finish. But if the program crashes when we editing the file, we will leave the file open. One thing we can do is to use the try catch block to catch the exception and close the file finally. C# has a short keyword for this purpose, the using keyword. It will call Dispose() function in IDisposable -> TextWriter -> StreamWriter to free up the memory and close the file.
1
2
3
4
using (var writer = File.AppendText($"{Name}.txt"))
{
writer.WriteLine(grade);
}