Introducing the C# Class Type


A class is a user-defined type composed of field data (member variables) and members that operate on this data. Collectively, the set of field data represents the "state" of a class instance. A class instance is otherwise known as an object. The power of object-oriented languages is grouping data and related functionality in unified class definition allows you to model your software after entities in the real world.

A class in C# is defined using the class keyword. After defining the class, you need to consider the set of member variables that will be used to represent its state. Note that field data of a class should seldom (if ever) be defined as public. It is far better design to define data as private and allow controlled access to the data via properties.

class Car
{
  public string petName;
  public int currSpeed;
}

Next is the set of member variables that model its behavior.

class Car
{
  public void PrintState()
    => Console.WriteLine("{0} is going {1} MPH.", petName, currSpeed);

  public void SpeedUp(int delta)
    => currSpeed += delta;
}

Allocating Objects with the new Keyword


Objects must be allocated into memory using the new keyword.

Understanding Constructors


Programmers typically want to assign relevant values to the object's field data before use. It is not uncommon for a class to have dozens of fields of data to contend with.

C# supports the use of constructors, which allow the state of an object to be established at the time of creation. A constructor is a special method of a class that is called indirectly when creating an object using the new keyword. Constructors will never have a return value (not even void) and are always named identically to the class that they are constructing.

Understanding the Role of the Default Constructor


Every C# class is provided with a default constructor that you can redefine. By definition, a default constructor never takes arguments. If you are not satisfied with the default constructor, here is an example of how to update the class:

class Car
{
  public string petName;
  public int currSpeed;
  
  public Car()
  {
    petName = "Chuck";
    currSpeed = 10;
  }
...
}

Defining Custom Constructors


What makes one constructor different from another in the eyes of the C# compiler is the number and/or type of the constructor arguments. When methods are defined with the same name but differ in the number or type of arguments, the methods have been overloaded. Overloading the constructor provides a way to have more than one constructor.

Constructors As Expression-Bodied Members


C# 7 added additional uses for the expression-bodied member style. Constructors, finalizers, and get/set accessors on properties and indexers now accept the new syntax.

public Car(String pn) => petName = pn;

Constructors with out Parameters


Constructors can use out parameters.

public Car(string pn, int cs, out bool inDanger)
{
  petName = pn;
  currSpeed = cs;
  if (cs > 100)
  {
    inDanger = true;
  }
  else
  {
    inDanger = false;
  }
}

Understanding the Default Constructor Revisited


All class are provided with a free default constructor. As soon as you define a custom constructor with any number of parameters, the default constructor is silently removed from the class and is no longer available. Therefore if you want to have a different constructor as well as a default constructor, you must define the default.

Understanding the Role of the this Keyword


The this keyword provides access to the current class instance. This can be used to resolve scope ambiguity, for example when an incoming parameter is named identically to a data field of the class.

A common naming convention is to start private class-level variable names with an underscore ( e.g. _driverName).

Chaining Constructor Calls Using this


Another use of the this keyword is to design a class using a technique termed constructor chaining. This design pattern is helpful when you have a class that defines multiple constructors.

Observing Constructor Flow


Revisiting Optional Arguments


Understanding the static Keyword


A C# class may define any number of static members which are declared using the static keyword. These members must be invoked from the class, rather than from an object. Static members are items that are deemed (by the class designer) to be so commonplace that there is no need to create an instance of the class before invoking the member. They are quite commonly found in utility classes. By definition, a utility class is a class that does not maintain any object-level state and is never created with the new keyword. It only exposes functionality with static members. Console, Math, Environment, and GC are a few of the utility classes in the .NET Core base class libraries.

Defining Static Field Data


Defining Static Methods


Defining Static Constructors


Defining Static Classes


Importing Static Members via the C# using Keyword


Defining the Pillars of OOP


All object-oriented languages contend with the three core principles which are called the pillars of object-oriented programming: encapsulation, inheritance, and polymorphism.

Understanding the Role of Encapsulation


Encapsulation boils down to the language's ability to hide unnecessary implementation details from the user. Closely related is the idea of data protection. Ideally, an object's state data should be specified using the private, internal, or protected keywords.

Understanding the Role of Inheritance


Inheritance boils down to the language's ability to allow you to build new class definitions based on existing class definitions. System.Object is always the topmost parent in any class hierarchy in .NET.

Understanding the Role of Polymorphism


Polymorphism is a language's ability to treat related objects in a similar manner. A base class can define a set of members (formally termed the polymorphic interface) that are available to all descendants. A class's polymorphic interface is constructed using any number of virtual or abstract members.

A virtual member is a member in a base class that defines a default implementation that may be changed (or more formally speaking, overridden) by a derived class. In contrast, an abstract method is a member in a base class that does not provide a default implementation but does provide a signature. An abstract method must be overridden by a derived type.

Understanding C# Access Modifiers


Types (classes, interfaces, structures, enumerations, and delegates) and their members (properties, methods, constructors, and fields) are defined using a keyword that specifies how visible that item is to other parts of the application.



Using the Default Access Modifiers


By default, type members are implicitly private, while types are implicitly internal.

class Radio
{
  Radio(){}
}

In the above example, the class is implicitly internal and the constructor is implicitly private.

The public keyword must be used to allow other parts of the program to invoke members of an object.

Using Access Modifiers and Nested Types


A nested type is a type declared directly within the scope of a class or structure.

Understanding the First Pillar: C#'s Encapsulation Services


Public fields (except for public constants and public read-only fields) typically have no place in a production-level class definition. Members of a class that represent an object's state should not be marked as public.

Encapsulation provides a way to preserve the integrity of an object's state data. You should get in the habit of defining private data which is indirectly manipulated either through a pair of public accessor (get) and mutator (set) methods or a public property.

A well-encapsulated class should protect its data and hide the details of how it operates from the outside world. This can be called black-box programming. This also provides the benefit of allowing the object to change how a given method is implemented without breaking any existing code making use of it.

Encapsulation Using Traditional Accessors and Mutators


A traditional approach to allow interacting with certain private fields of a class is to define an accessor (get method) and a mutator (set method).

Encapsulation using Properties


.NET Core languages prefer to use properties for this. Properties are just a container for "real" accessor and mutator methods, named get and set, respectively.

class Employee
{
  // Field data
  private string _empName;
  private int _empId;
  private float _currPay;
  // Properties
  public string Name
  {
    get { return _empName; }
    set {
      if (value.Length > 15)
      {
        Console.WriteLine("Error");
      }
      else
      {
        _empName = value;
      }
    }
  }
  public int Id
  {
    get { return _empId; }
    set { _empId = value; }
  }
}

A C# property is defined by a get scope and a set scope directly within the property itself. Note how properties do not make use of parentheses (like a method would) when being defined.

Within the set scope of a property, you use a token named value which represents the incoming value used to assign the property by the caller. It is not a true C# keyword, rather it is what is called a contextual keyword.

public string Name
{
  get { return _empName; }
  set {
    if (value.Length > 15)
    {  Console.WriteLine("error");
    }
    else
    {
      empName = value;
    }
  }
}

Properties (as opposed to accessor and mutator methods) make your types easier to manipulate in that properties are able to respond to the intrinsic operators of C#. For example, you can use the ++ operator on a property.

Properties As Expression-Bodied Members


Property get and set accessors can also be written as expression-bodied members.

public int Age
{
  get => empAge;
  set => empAge = value;
}

Using Properties Within a Class Definition


Properties (specifically the set portion) are common places to package up the business rules of your class. Suppose you had some check in the constructor and also the property: that is duplicated code. To isolate all of the checking to one location, you can always use the properties in the constructor and anywhere in the class. You might always use properties within your class whenever you need to get or set the values. This ensures that the business rules are always enforced.

Read-Only Properties


You can configure a property to be read-only by omitting the set block. Properties that only have a getter can also be simplified using expression body members. The following two snippets are equivalent:

public string SocialSecurityNumber
{
  get { return _empSSN; }
}

public string SocialSecurityNumber => _empSSN;

Write-Only Properties


Omit the get block.

Mixing Private and Public Get/Set Methods on Properties


public string SocialSecurityNumber
{
  get => _empSSN;
  private set => _empSSN = value;
}

Revisiting the static Keyword: Defining Static Properties


class SavingsAccount
{
  public static double InterestRate
  {
    get { return _currInterestRate; }
    set { _currInterestRate = value; }
  }
...
}

Pattern Matching with Property Patterns


Extended Property Patterns


Understanding Automatic Properties


It can get rather verbose when you have a lot of the same simple property definitions. To streamline the process of providing simple encapsulation of field data, you can use automatic property syntax.

class Car
  // Automatic properties! No need to define backing fields.
  public string PetName { get; set; }
  public int Speed { get; set; }
  public string Color { get; set; }
}

Visual Studio and VS Code both provide the prop code snippet. Type prop inside a class definition and Tab twice to generate starter code for a new automatic property.

Note that the only way to see the name of the autogenerated private backing field is by making use of a tool such as ildasm.exe.

It is possible to define a "read-only automatic property" by omitting the set scope. It is not possible to define a write-only automatic property.

Interacting with Automatic Properties


A class defining automatic properties will always need to use property syntax to get and set the underlying values.

Automatic Properties and Default Values


Automatic properties that encapsulate numerical or Boolean data will have hidden backing fields automatically assigned a safe default value like 0 for numerical data and false for Booleans. If you use automatic property syntax to wrap another class variable, the hidden private reference type will also be set to a default value of null which can be problematic if you are not careful.

Initializing Automatic Properties


Recall that a data field of a class can be directly assigned an initial value upon declaration:

class Car
{
  private int numberOfDoors = 2;
}

In a similar manner, C# allows you to assign an initial value to the underlying backing field generated by the compiler.

class Garage
  // The hidden backing field is set to 1
  public int NumberOfCars { get; set; } = 1;

  // The hidden backing field is set to a new Car object.
  public Car MyAuto { get; set; } = new Car();

  public Garage(){}
  public Garage(Car car, int number)
  {
    MyAuto = car;
    NumberOfCars = number;
  }
}

Understanding Object Initialization


A constructor allows you to specify startup values when creating a new object. Related to that, properties allow you to get and set underlying data in a safe manner. When using code from a library, it is common for there not to be a specific constructor to set every underlying state data you need to. So typically you will choose the best constructor possible and then make assignments using a handful of provided properties.

Looking at the Object Initialization Syntax


C# offers object initializer syntax to help streamline the process of getting an object up and running. It is possible to create a new object variable and assign a slew of properties and/or public fields in a few lines of code using this syntax. Syntactically, the object initializer consists of a comma-delimited list of specified values enclosed by the { and } tokens. Each member in the initialization list maps to the name of a public field or public property in the object being initialized.

// Make a Point by setting each property manually.
Point firstPoint = new Point();
firstPoint.X = 10;
firstPoint.Y = 10;

// Or make a Point via a custom constructor
Point anotherPoint = new Point(20, 20);

// Or make a Point using object init syntax
Point finalPoint = new Point { X = 30, Y = 30 };

Behind the scenes when using the object initialization syntax, the type's default constructor is invoked, followed by setting the values to the specified properties. Therefore the object initialization syntax is just a shorthand notation for the syntax used to create a class variable using a default constructor and to set the state data property by property.

It is important to remember that the object initialization process is using the property setter implicitly. If the property setter is marked private, this syntax cannot be used.

Using init-Only Setters


A new feature in C# 9.0 is init-only setters. These setters enable a property to have its value set during initialization, but after construction is complete, the property becomes read-only. These types of properties are called immutable.

class PointReadOnlyAfterCreation
{
  public int X { get; init; }
  public int Y { get; init; }
}

Calling Custom Constructors with Initialization Syntax


Be aware that when you are constructing a type using object initialization syntax, you are able to invoke any constructor defined by the class.

Point pt = new Point(10, 16) { X = 100, Y = 100 };

Initializing Data with Initialization Syntax


Rectangle myRect = new Rectangle
{
  TopLeft = new Point { X = 10, Y = 10},
  BottomRight = new Point { X = 200, Y = 200}
};

Working with Constant and Read-Only Field Data


Sometimes you want an immutable property (a property that you do not want changed at all, either from the time it was compiled or after it was set during construction).

Understanding Constant Field Data


C# offers the const keyword to define constant data, which can never change after the initial assignment. Constant fields of a class are implicitly static.

class MyMathClass
{
  public const double PI = 3.14;
}

The thing to always remember is that the initial value assigned to the constant must be specified at the time you define the constant. Trying to assign the value of the PI above in the constructor would throw an error because the constructor is invoked at runtime, and the value of constant data must be known at compile time.

Constant Interpolated Strings


Introduced in C# 10, const string values can use string interpolation in their assignment statements as long as all of the components that are used are also const strings. 

Understanding Read-Only Fields


Closely related to constant data is read-only field data, which should not be confused with read-only properties. A read-only field cannot be changed after the initial assignment or you will receive a compile-time error. Unlike a constant, the value assigned to a read-only field can be determined at runtime. Therefore it can be legally assigned within the scope of a constructor but nowhere else.

This can be helpful when reading an external file to obtain a value but you need to ensure the value will not change after that point.

class MyMathClass
{
  // Read-only fields can be assigned in constructors,
  // but nowhere else.
  public readonly double PI;

  public MyMathClass ()
  {
    PI = 3.14;
  }
}

Understanding Static Read-Only Fields


Unlike a constant field, a read-only field is not implicitly static. If you want to expose the read-only field from the class level, you must explicitly use the static keyword.

Understanding Partial Classes


The partial keyword allows for a single class to be partitioned across multiple code files. When you scaffold Entity Framework Core classes from a database, the created classes are all created as partial classes. This may represent an intermediate stage toward refactoring a class that has grown over time into something difficult to manage.

Each part of the partial class must be marked with the partial keyword. Partial classes make no difference after compilation; the whole idea is realized only during design time. The only requirement when defining partial types is that the type's name is identical and defined within the same .NET Core namespace.

Recall that any methods in top-level statements must be a local function. The top-level statements are implicitly defined in a partial Program class, allowing for the creation of another partial Program class to hold regular methods.

Records




Article notes

What are a special reference type in C# that provide methods for equality using value semantics and data encapsulation?
What is a user-defined type composed of field data (member variables) and members that operate on this data (C# terminology)?
In the eyes of the C# compiler, what makes the different constructors of a class different?
What is an instance of a class otherwise known as (C#)?
The power of object-oriented languages allowing you to group data and related functionality in unified class definitions is that it allows you to do what?
What keyword in C# provides access to the current class instance?
What methods in C# allow the state of an object to be established at the time of creation?
public Car(string pn) => petName = pn; This C# constructor is an example of what type of member?
What is the "freebie" that every C# class is provided, which never takes arguments, but allows all field data of the class to be set to an appropriate default value, and you can redefine it if you want?
How can you redefine the default constructor of a C# class?
Record types in C# are really one specialized type of what?
C# supports mutable record types by using standard (not init-only) setters, but record types are really intended to be used for data models that are what?
How is a constructor named in C#?
What is a special method of a class that is called indirectly when creating an object using the new keyword in C#?
What is the keyword used to define a class in C#?
What is the keyword in C# to allocate an object into memory?
What are supported by C# and allow the state of an object to be established at the time the object is created?
What is a special method of a class that is called indirectly when creating an object using the new keyword (C#)?
What kind of members must be invoked from the class in C# (rather than an object)?
Members of a class deemed to be so commonplace that there is no need to create an instance of a class will be declared as what kind of members (C#)?
What kind of class is a class that does not maintain any object-level state and is never created with the new keyword?
What kind of members does a C# utility class expose functionality with?
What are the three pillars of OOP that all object-oriented programming languages must content with?
What pillar of OOP boils down to the language's ability to hide implementation details from the user?
What pillar of OOP boils down to the language's ability to allow you to build new class definitions based on existing class definitions?
What is the topmost parent class in any and every class hierarchy in .NET?
What pillar of OOP boils down to the language's ability to treat related objects in a similar way?
What type of member in a C# base class defines a default implementation that may be overriden by a base class?
What type of member in a C# base class provides only a signature and must be overridden by a derived type?
In C#, properties, methods, constructors, and fields are all considered what?
What is the default applied access modifier for a type in C#?
What is the default applied access modifier for a type member in C#?
What is a type called when it is declared directly within the scope of a class or structure in C#?
The traditional approach to allow interacting with certain private fields of a class is to define 2 methods called what?
What is the language feature preferred to use for encapsulation over accessor and mutator methods in .NET Core languages?
What type members in C# are just a container for "real" accessor and mutator methods, named get and set?
What is the token, or contextual keyword (not a true C# keyword), that can be used within the set scope of a property to represent the incoming value used to assign the property by the caller?
How do you make a property read-only in C#?
How do you make a property write-only in C#?
What C# feature can be used to streamline a lot of repetitive, basic properties?
What can you type in both Visual Studio and Visual Studio Code (and hit tab twice) to generate starter code for a new automatic property?
What is the shorthand notation offered by C# used to create a class variable using a constructor and then set the state data by property?
Can the object initialization syntax in C# be used to set a property where the property setter is marked private?
What keyword does C# offer to define constant data, which can never change after the initial assignment, and the value of which must be known at compile time?
Can a constant in C# have its value determined at runtime?
Why can the value of a constant in C# not be assigned inside a constructor?
What is the condition for a const string value to use string interpolation in their assignment statement (new in C# 10)?
How is a read-only field different from a constant in C#?
What is the keyword in C# that allows a single class to be partitioned across multiple code files?
When defining a class in C# across multiple files, each part must be marked with what keyword?
What is the special reference type that provides synthesized methods for equality using value semantics and data encapsulation in C#?
What is the following equivalent to (C#)? record CarRecord
Defining a record like this in C# removes the need for what property? record CarRecord(string Color);
What method do record types using positional parameters have (a method with an out parameter for each positional parameter in the declaration)?
How do record types in C# behave with Equals(), ==, and !=?
What is the construct in C# that can be used to create a true copy of a record with one or more properties modified?
What are the value type equivalent of record types (new in C# 10)?
Previous Next