kota's memex

public

Allows a field or method to be used / called from outside the class.

private

Prevents a field or method to be used / called from outside the class. If no accessibility level is provided it is defaulted to private. Fields should generally be private as a rule of thumb.

internal

Like public, but limited to a project. When creating a type itself the default accessibility option is internal.

readonly

The readonly modifier is used in conjunction with an access level and is used to create an immutable field. They can be assigned a value as an initializer or in a constructor, but nowhere else:

public class Player
{
  private readonly string _name;

  public Player(string name)
  {
    _name = name;
  }
}

required

public class Person
{
  public required string LastName { get; set; }
  public required string FirstName { get; set; }

  var p1 = new Person(); // Error! Required properties not set
  var p2 = new Person() { FirstName = "Grace", LastName = "Hopper" };
}

static

When using Console, Convert, and Math we do not need to call new Console() first. Members of a class can be marked static which makes them owned by the type / class itself rather than an individual object. If an entire class is marked static, as is the case with Console and the others, all of its members must be marked static.

This is used to create "global" like variables, but tied to a specific class:

public class Score
{
  private static readonly int PointThreshold = 1000; // Points needed to win.
  private static readonly int LevelThreshold = 4; // Level needed to win.
}

Static fields are usually named with UpperCamelCase rather than _lowerCamelCase.

A static constructor can also be created, which is not called directly and cannot have parameters, but instead runs automatically the first time the class is used. It's a bit like go's init() function.

protected

The protected modifier allows usage from within the class or any derived classes.

virtual and override

Allows derived classes to "override" the implementation of a given method. This is one of the main ways to achieve polymorphism in c#. Take this base class of a chess piece for example:

public class ChessPiece
{
  public int Row { get; set; }
  public int Column { get; set; }

  public virtual bool IsLegalMove(int row, int column)
  {
    return IsOnBoard(row, column) && !IsCurrentLocation(row, column);
  }

  public bool IsOnBoard(int row, int column)
  {
    return row >= 0 && row < 8 && column >= 0 && column < 8;
  }

  public bool IsCurrentLocation(int row, int column)
  {
    return row == Row && column == Column;
  }
}

Because IsLegalMove is a virtual method we can override the implementation for out KingPiece derived class:

public class KingPiece : ChessPiece
{
  public override bool IsLegalMove(int row, int column)
  {
    if (!base.IsLegalMove(row, column)) return false;

    if (Math.Abs(row - Row) > 1) return false;
    if (Math.Abs(column - Column) > 1) return false;

    return true;
  }
}