Reader small image

You're reading from  C# 12 and .NET 8 – Modern Cross-Platform Development Fundamentals - Eighth Edition

Product typeBook
Published inNov 2023
PublisherPackt
ISBN-139781837635870
Edition8th Edition
Right arrow
Author (1)
Mark J. Price
Mark J. Price
author image
Mark J. Price

Mark J. Price is a Microsoft Specialist: Programming in C# and Architecting Microsoft Azure Solutions, with over 20 years' experience. Since 1993, he has passed more than 80 Microsoft programming exams and specializes in preparing others to pass them. Between 2001 and 2003, Mark was employed to write official courseware for Microsoft in Redmond, USA. His team wrote the first training courses for C# while it was still an early alpha version. While with Microsoft, he taught "train-the-trainer" classes to get other MCTs up-to-speed on C# and .NET. Mark holds a Computer Science BSc. Hons. Degree.
Read more about Mark J. Price

Right arrow

Controlling access with properties and indexers

Earlier, you created a method named GetOrigin that returned a string containing the name and origin of the person. Languages such as Java do this a lot. C# has a better way, and it is called properties.A property is simply a method (or a pair of methods) that acts and looks like a field when you want to get or set a value, but it acts like a method, thereby simplifying the syntax and enabling functionality, like validation and calculation, when you set and get a value.

A fundamental difference between a field and a property is that a field provides a memory address to data. You could pass that memory address to an external component, like a Windows API C-style function call, and it could then modify the data. A property does not provide a memory address to its data, which provides more control. All you can do is ask the property to get or set the data. The property then executes statements and can decide how to respond, including refusing...

Pattern matching with objects

In Chapter 3, Controlling Flow, Converting Types, and Handling Exceptions, you were introduced to basic pattern matching. In this section, we will explore pattern matching in more detail.

Pattern-matching flight passengers

In this example, we will define some classes that represent various types of passengers on a flight, and then we will use a switch expression with pattern matching to determine the cost of their flight:

  1. In the PacktLibraryNetStandard2 project/folder, add a new file named FlightPatterns.cs.
  2. If you use Visual Studio 2022, in FlightPatterns.cs, delete the existing statements, including the class named FlightPatterns, because we will define multiple classes, and none match the name of the code file.
  3. In FlightPatterns.cs, add statements to define three types of passenger with different properties, as shown in the following code:
// All the classes in this file will be defined in the following namespace.
namespace Packt.Shared;
public class...

Working with record types

Before we dive into the new record language feature, let us see some other related new features of C# 9 and later.

Init-only properties

You have used object initialization syntax to instantiate objects and set initial properties throughout this chapter. Those properties can also be changed after instantiation.Sometimes, you want to treat properties like readonly fields so that they can be set during instantiation but not after. In other words, they are immutable. The init keyword enables this. It can be used in place of the set keyword in a property definition.Since this is a language feature not supported by .NET Standard 2.0, we cannot use it in the PacktLibraryNetStandard2 project. We must use it in the modern project:

  1. In the PacktLibraryModern project, add a new file named Records.cs.
  2. In Records.cs, define a person class with two immutable properties, as shown in the following code:
namespace Packt.Shared;
public class ImmutablePerson
{
  public string...

Practicing and exploring

Test your knowledge and understanding by answering some questions, getting some hands-on practice, and exploring this chapter's topics with deeper research.

Exercise 5.1 – Test your knowledge

Answer the following questions:

  1. What are the seven access modifier keywords and combinations of keywords, and what do they do?
  2. What is the difference between the static, const, and readonly keywords when applied to a type member?
  3. What does a constructor do?
  4. Why should you apply the [Flags] attribute to an enum type when you want to store combined values?
  5. Why is the partial keyword useful?
  6. What is a tuple?
  7. What does the record keyword do?
  8. What does overloading mean?
  9. What is the difference between the following two statements? (Do not just say a ">" character!)
public List<Person> Children = new();
public List<Person> Children => new();
  1. How do you make a method parameter optional?

Exercise 5.2 – Practice with access modifiers...

Summary

In this chapter, you learned about:

  • Making your own types using OOP.
  • Some of the different categories of members that a type can have, including fields to store data and methods to perform actions.
  • OOP concepts, such as aggregation and encapsulation
  • How to use modern C# features, like relational and property pattern matching enhancements, init-only properties, and record types.

In the next chapter, you will take these concepts further by defining operators, delegates, and events, implementing interfaces, and inheriting from existing classes.

Implementing interfaces

Interfaces are a way to implement standard functionality and connect different types to make new things. Think of them like the studs on top of LEGO™ bricks, which allow them to “stick” together, or electrical standards for plugs and sockets.

If a type implements an interface, then it makes a promise to the rest of .NET that it supports specific functionality. Therefore, they are sometimes described as contracts.

Common interfaces

Table 6.1 shows some common interfaces that your types might implement:

Interface

Method(s)

Description

IComparable

CompareTo(other)

This defines a comparison method that a type implements to order or sort its instances.

...

Managing memory with reference and value types

I mentioned reference types a couple of times. Let’s look at them in more detail.

Understanding stack and heap memory

There are two categories of memory: stack memory and heap memory. With modern operating systems, the stack and heap can be anywhere in physical or virtual memory.

Stack memory is faster to work with but limited in size. It is fast because it is managed directly by the CPU and it uses a last-in, first-out mechanism, so it is more likely to have data in its L1 or L2 cache. Heap memory is slower but much more plentiful.

On Windows, for ARM64, x86, and x64 machines, the default stack size is 1 MB. It is 8 MB on a typical modern Linux-based operating system. For example, in a macOS or Linux terminal, I can enter the command ulimit -a to discover that the stack size is limited to 8,192 KB and that other memory is “unlimited.” This limited amount of stack memory is why it is so easy to fill...

Working with null values

You have seen how reference types are different from value types in how they are stored in memory, as well as how to store primitive values like numbers in struct variables. But what if a variable does not yet have a value? How can we indicate that? C# has the concept of a null value, which can be used to indicate that a variable has not been set.

Making a value type nullable

By default, value types like int and DateTime must always have a value, hence their name. Sometimes, for example, when reading values stored in a database that allows empty, missing, or null values, it is convenient to allow a value type to be null. We call this a nullable value type.

You can enable this by adding a question mark as a suffix to the type when declaring a variable.

Let’s see an example. We will create a new project because some of the null handling options are set at the project level:

  1. Use your preferred coding tool to add a new Console...

Inheriting from classes

The Person type we created earlier derived (inherited) from System.Object. Now, we will create a subclass that inherits from Person:

  1. In the PacktLibrary project, add a new class file named Employee.cs.
  2. Modify its contents to define a class named Employee that derives from Person, as shown in the following code:
    namespace Packt.Shared;
    public class Employee : Person
    {
    }
    
  3. In the PeopleApp project, in Program.cs, add statements to create an instance of the Employee class, as shown in the following code:
    Employee john = new()
    {
      Name = "John Jones",
      Born = new(year: 1990, month: 7, day: 28,
        hour: 0, minute: 0, second: 0, offset: TimeSpan.Zero))
    };
    john.WriteToConsole();
    
  4. Run the PeopleApp project and view the result, as shown in the following output:
    John Jones was born on a Saturday.
    

Note that the Employee class has inherited all the members of Person.

Extending...

Casting within inheritance hierarchies

Casting between types is subtly different from converting between types. Casting is between similar types, like between a 16-bit integer and a 32-bit integer, or between a superclass and one of its subclasses. Converting is between dissimilar types, such as between text and a number.

For example, if you need to work with multiple types of stream, then instead of declaring specific types of stream like MemoryStream or FileStream, you could declare an array of Stream, the supertype of MemoryStream and FileStream.

Implicit casting

In the previous example, you saw how an instance of a derived type can be stored in a variable of its base type (or its base’s base type, and so on). When we do this, it is called implicit casting.

Explicit casting

The opposite of implicit casting is explicit casting, and you must use parentheses around the type you want to cast into as a prefix to do it:

  1. In Program.cs, add a statement...

Inheriting and extending .NET types

.NET has pre-built class libraries containing hundreds of thousands of types. Rather than creating your own completely new types, you can often get a head start by deriving from one of Microsoft’s types to inherit some or all its behavior, and then overriding or extending it.

Inheriting exceptions

As an example of inheritance, we will derive a new type of exception:

  1. In the PacktLibrary project, add a new class file named PersonException.cs.
  2. Modify the contents of the file to define a class named PersonException with three constructors, as shown in the following code:
    namespace Packt.Shared;
    public class PersonException : Exception
    {
      public PersonException() : base() { }
      public PersonException(string message) : base(message) { }
      public PersonException(string message, Exception innerException)
        : base(message, innerException) { }
    }
    

    Unlike ordinary methods, constructors are not inherited...

Summarizing custom type choices

Now that we have covered OOP and the C# features that enable you to define your own types, let’s summarize what you’ve learned.

Categories of custom types and their capabilities

Categories of custom types and their capabilities are summarized in the following table:

Type

Instantiation

Inheritance

Equality

Memory

class

Yes

Single

Reference

Heap

sealed class

Yes

None

Reference

Heap

...

Practicing and exploring

Test your knowledge and understanding by answering some questions, getting some hands-on practice, and exploring this chapter’s topics with more in-depth research.

Exercise 6.1 – Test your knowledge

Answer the following questions:

  1. What is a delegate?
  2. What is an event?
  3. How are a base class and a derived class related, and how can the derived class access the base class?
  4. What is the difference between is and as operators?
  5. Which keyword is used to prevent a class from being derived from or a method from being further overridden?
  6. Which keyword is used to prevent a class from being instantiated with the new keyword?
  7. Which keyword is used to allow a member to be overridden?
  8. What’s the difference between a destructor and a deconstruct method?
  9. What are the signatures of the constructors that all exceptions should have?
  10. What is an extension method, and how do you define one...

Summary

In this chapter, you learned about:

  • Operators
  • Generic types
  • Delegates and events
  • Implementing interfaces
  • Memory usage differences between reference and value types
  • Working with null values
  • Deriving and casting types using inheritance
  • Base and derived classes, how to override a type member, and using polymorphism

In the next chapter, you will learn how .NET is packaged and deployed, and in subsequent chapters, the types that it provides you with to implement common functionality, such as file handling and database access.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:

https://packt.link/csharp12dotnet8

lock icon
The rest of the chapter is locked
You have been reading a chapter from
C# 12 and .NET 8 – Modern Cross-Platform Development Fundamentals - Eighth Edition
Published in: Nov 2023Publisher: PacktISBN-13: 9781837635870
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Mark J. Price

Mark J. Price is a Microsoft Specialist: Programming in C# and Architecting Microsoft Azure Solutions, with over 20 years' experience. Since 1993, he has passed more than 80 Microsoft programming exams and specializes in preparing others to pass them. Between 2001 and 2003, Mark was employed to write official courseware for Microsoft in Redmond, USA. His team wrote the first training courses for C# while it was still an early alpha version. While with Microsoft, he taught "train-the-trainer" classes to get other MCTs up-to-speed on C# and .NET. Mark holds a Computer Science BSc. Hons. Degree.
Read more about Mark J. Price