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?
Records
What is a user-defined type composed of field data (member variables) and members that operate on this data (C# terminology)?
A class
In the eyes of the C# compiler, what makes the different constructors of a class different?
Number and type of the constructor arguments
What is an instance of a class otherwise known as (C#)?
An object
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?
Model your software after entities in the real world
What keyword in C# provides access to the current class instance?
this
What methods in C# allow the state of an object to be established at the time of creation?
Constructors
public Car(string pn) => petName = pn;
This C# constructor is an example of what type of member?
An expression-bodied 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?
The default constructor
How can you redefine the default constructor of a C# class?
Define a constructor that takes no arguments
Record types in C# are really one specialized type of what?
class
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?
Immutable
How is a constructor named in C#?
Identical to the class they construct
What is a special method of a class that is called indirectly when creating an object using the new keyword in C#?
A constructor
What is the keyword used to define a class in C#?
class
What is the keyword in C# to allocate an object into memory?
new
What are supported by C# and allow the state of an object to be established at the time the object is created?
Constructors
What is a special method of a class that is called indirectly when creating an object using the new keyword (C#)?
A constructor
What kind of members must be invoked from the class in C# (rather than an object)?
Static
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#)?
Static
What kind of class is a class that does not maintain any object-level state and is never created with the new keyword?
A utility class
What kind of members does a C# utility class expose functionality with?
Static
What are the three pillars of OOP that all object-oriented programming languages must content with?
Encapsulation, inheritance, and polymorphism
What pillar of OOP boils down to the language's ability to hide implementation details from the user?
Encapsulation
What pillar of OOP boils down to the language's ability to allow you to build new class definitions based on existing class definitions?
Inheritance
What is the topmost parent class in any and every class hierarchy in .NET?
System.Object
What pillar of OOP boils down to the language's ability to treat related objects in a similar way?
Polymorphism
What type of member in a C# base class defines a default implementation that may be overriden by a base class?
virtual
What type of member in a C# base class provides only a signature and must be overridden by a derived type?
abstract
In C#, properties, methods, constructors, and fields are all considered what?
Type members
What is the default applied access modifier for a type in C#?
internal
What is the default applied access modifier for a type member in C#?
private
What is a type called when it is declared directly within the scope of a class or structure in C#?
A nested type
The traditional approach to allow interacting with certain private fields of a class is to define 2 methods called what?
An accessor and a mutator
What is the language feature preferred to use for encapsulation over accessor and mutator methods in .NET Core languages?
Properties
What type members in C# are just a container for "real" accessor and mutator methods, named get and set?
Properties
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?
value
How do you make a property read-only in C#?
Omit the set block
How do you make a property write-only in C#?
Omit the get block
What C# feature can be used to streamline a lot of repetitive, basic properties?
Automatic property syntax
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?
prop
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?
The object initialization syntax
Can the object initialization syntax in C# be used to set a property where the property setter is marked private?
No
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?
const
Can a constant in C# have its value determined at runtime?
No
Why can the value of a constant in C# not be assigned inside a constructor?
A constructor is invoked at runtime and the value of constant data must be known at compile time
What is the condition for a const string value to use string interpolation in their assignment statement (new in C# 10)?
All the components used must also be const strings
How is a read-only field different from a constant in C#?
A read-only field can have a value assigned at runtime and a read-only field is not implicitly static
What is the keyword in C# that allows a single class to be partitioned across multiple code files?
partial
When defining a class in C# across multiple files, each part must be marked with what keyword?
partial
What is the special reference type that provides synthesized methods for equality using value semantics and data encapsulation in C#?
Record
What is the following equivalent to (C#)?
record CarRecord
record class CarRecord
Defining a record like this in C# removes the need for what property?
record CarRecord(string Color);
public string Color { get; init; }
What method do record types using positional parameters have (a method with an out parameter for each positional parameter in the declaration)?
Deconstruct()
How do record types in C# behave with Equals(), ==, and !=?
They compare like value types
What is the construct in C# that can be used to create a true copy of a record with one or more properties modified?
with
Example:
CarRecord ourOtherCar = myCarRecord with {Model = "Odyssey"};
What are the value type equivalent of record types (new in C# 10)?
Record structs