C# · STEP 2 · OOP

C# · OOP

C# 14 on .NET 10 (anything from .NET 8 up works): classes, constructors, properties, methods, inheritance and static members — the JavaScript version next to the C# version.

Step 2 of 5ASP.NET Core / Entity Framework Core© Arthur Bernier Jrrewritten · verified10 code blocks421 words

What you will learn#

  • How to Create a Class
  • How to Instantiate a Class
  • Write a Constructor and Methods
  • Create an Inherited Class
  • Write Static Methods/Properties

Below you'll see JavaScript examples of object-oriented patterns and their C# counterparts. Each C# block is a complete Program.cs: the statements at the top run; the classes they use are declared underneath (top-level statements must come first).

Creating and Instantiating a Class#

In Javascript

JS
// Creating a Classclass Dog {} // Instantiationconst Sparky = new Dog();

In C#

C#
// Instantiating the class: the type on the left, new on the rightDog sparky = new Dog(); // Creating a new class (declared after the statements that use it)class Dog { }

Convention: classes, properties and methods are PascalCase; locals and parameters camelCase. The parentheses after new Dog are required even with nothing to pass.

The Constructor and Methods#

In Javascript

JS
// Creating a Classclass Dog {  // The constructor runs when we instantiate a new instance  constructor(name, age) {    this.name = name;    this.age = age;  }   // A Method  bark() {    console.log(`${this.name} barks`);  }} // Instantiationconst Sparky = new Dog("Sparky", 4);Sparky.bark();

in C#

C#
// Instantiating the classDog sparky = new Dog("Sparky", 4);// Using the instance Bark methodsparky.Bark(); // Creating a new classclass Dog{    // Properties: typed, PascalCase. { get; set; } makes them readable and writable from outside    public string Name { get; set; }    public int Age { get; set; }     // The constructor is a method bearing the class's name, with no return type    public Dog(string name, int age)    {        Name = name;   // `this.Name` works too; it is only needed when names clash        Age = age;    }     // A method: access modifier, return type, PascalCase name    public void Bark()    {        Console.WriteLine($"{Name} barks");    }}

WHY public: C# members are private unless you say otherwise — the opposite of JavaScript. Properties with { get; set; } are the normal way to expose data; { get; } alone makes one read-only after the constructor. Plain fields (string name;, what the 2021 original used) are for private state, usually named _name.

Printing an object shows its type name (Dog) unless you override ToString():

C#
public override string ToString() => $"Dog(Name={Name}, Age={Age})";

For classes that are mostly data, a record writes the constructor, properties, ToString() and value equality in one line: public record Dog(string Name, int Age);.

Inheritance#

In Javascript

JS
// Creating a Classclass Dog {  // The constructor runs when we instantiate a new instance  constructor(name, age) {    this.name = name;    this.age = age;  }   // A Method  bark() {    console.log(`${this.name} barks`);  }} class SmallDog extends Dog {  constructor(name, age) {    //Super calls the constructor of the parent class    super(name, age);  }   //This will override the version of bark in the parent class  bark() {    console.log(`${this.name} yaps!`);  }} // Instantiationconst Sparky = new SmallDog("Sparky", 4);Sparky.bark();

in C#

C#
// A SmallDog is a Dog, so it fits in a Dog-typed variable; the override still runsDog sparky = new SmallDog("Sparky", 4);sparky.Bark(); // Sparky yaps! // Creating a new classclass Dog{    public string Name { get; set; }    public int Age { get; set; }     public Dog(string name, int age)    {        Name = name;        Age = age;    }     // virtual: subclasses are allowed to replace this method    public virtual void Bark()    {        Console.WriteLine($"{Name} barks");    }} // The colon is C#'s `extends`; base(...) calls the parent constructorclass SmallDog : Dog{    public SmallDog(string name, int age) : base(name, age) { }     // override: replaces the parent's version    public override void Bark()    {        Console.WriteLine($"{Name} yaps!");    }}

Two keywords JavaScript doesn't make you write: the parent marks a method virtual (may be replaced) and the child marks it override (is replacing it); leave either out and the compiler tells you. protected members are visible to the class and its subclasses only — the 2021 version needed it for the fields; public properties don't. A class inherits from exactly one parent; for more contracts, use interfaces (class SmallDog : Dog, IComparable).

Static Methods and Properties#

In Javascript

JS
// Static properties/methods belong to the class, not the instanceclass Calculator {  static lastResult = 0;   // a table of operator functions instead of eval()  static operations = {    "+": (a, b) => a + b,    "-": (a, b) => a - b,    "*": (a, b) => a * b,    "/": (a, b) => a / b,  };   static calculate(num1, num2, operator) {    Calculator.lastResult = Calculator.operations[operator](num1, num2);    return Calculator.lastResult;  }   static showLastResult() {    console.log(Calculator.lastResult);  }} Calculator.calculate(2, 2, "+");Calculator.showLastResult();

In C#

C#
Calculator.Calculate(2, 2, "+");Calculator.ShowLastResult();                        // 4Console.WriteLine(Calculator.Calculate(5, 2, "/")); // 2.5 // Static members belong to the class, not to the instance; a static class cannot be instantiatedstatic class Calculator{    // a static property; private set means only this class can change it    public static double LastResult { get; private set; } = 0;     // a table of operator functions instead of eval(); Func<double, double, double> = "takes two doubles, returns one"    private static readonly Dictionary<string, Func<double, double, double>> Operations = new()    {        ["+"] = (a, b) => a + b,        ["-"] = (a, b) => a - b,        ["*"] = (a, b) => a * b,        ["/"] = (a, b) => a / b,    };     public static double Calculate(double num1, double num2, string op)    {        LastResult = Operations[op](num1, num2);        return LastResult;    }     public static void ShowLastResult()    {        Console.WriteLine(LastResult);    }}

C# has no eval (a good thing), but it has the switch expression, cousin of the switch statement the 2021 version used and the usual shape for a fixed set of cases:

C#
public static double Calculate(double num1, double num2, string op) => op switch{    "+" => num1 + num2,    "-" => num1 - num2,    "*" => num1 * num2,    "/" => num1 / num2,    _ => throw new ArgumentException($"Unknown operator: {op}"),};

WHY double: with int parameters 5 / 2 is 2 — integer division truncates in C#. (The original also declared calculate as void, so it stored the result but never returned it; the JavaScript version always did.)


Resources to Learn More#


See also