0% found this document useful (0 votes)
6 views

Lesson7 Report

Uploaded by

tibayankertyy
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Lesson7 Report

Uploaded by

tibayankertyy
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 123

LESSON 6:

BUILDING YOUR OWN TYPES


WITH OBJECT ORIENTED
PROGRAMMING WITH C#
CONTEN
TS: 1. Talking about OOP 9. Raising and handling events

2. Building class libraries 10. Making types safely reusable


with generics
3. Storing data with fields 11. Implementing interfaces

4. Writing and calling methods 12. Managing memory with


reference and value types
5. Controlling access with 13. Working with null values
properties and indexers
6. Pattern matching with objects 14. Inheriting from classes

7. Working with records 15. Casting within inheritance


hierarchies
8. Setting up a class library and 16. Inheriting and extending .NET
console application types
01
Talking about
OOP
Object-Oriented
Programming
OOP is a programming model that(OOP)
organizes software design
around data, or objects, rather than functions and logic. In C#,
OOP is fundamental because it allows developers to represent
real-world entities and create a hierarchy of classes that reflect
relationships between objects.

Key OOP Concepts in C#:


• Encapsulation: This restricts access to certain
components and is implemented by making fields private
and providing public methods or properties for access. This
encapsulation protects the object’s data integrity and hides
unnecessary implementation details.
• Abstraction: Hides the complex implementation details
and only exposes essential functionality, making it easier to
work with objects without needing to understand their inner
workings.
• Inheritance: This allows creating a new class (derived)
based on an existing class (base), promoting code
reusability and establishing a hierarchy.
• Polymorphism: Enables objects to be treated as instances
02
Building Class
Libraries
Building Class Libraries
Building class libraries in C# is a great way to organize and reuse code across
multiple projects. A class library is essentially a collection of classes,
interfaces, and methods compiled into a .dll file (Dynamic Link Library) that
other applications can reference and use.

Steps to Build a Class Library in C#


1. Create a New Class Library Project
• In Visual Studio, go to File > New > Project.
• Select Class Library (.NET Core or .NET Framework).
• Name your project, choose a location, and click Create.

2. Define Classes and Methods in the Library


• Once the project is created, you'll see a default class file ( classl.cs ). You
can rename it or add new classes.
• Define your classes, methods, and properties inside these files as needed.
• Ensure each class or method you want to be accessible from outside the
library is
marked public .
Building Class Libraries
3. Build the Project
• Right-click on the project in the Solution Explorer and select Build.
• After building, Visual Studio generates a .dll file in the bin/Debug (or
bin/Re1ease folder of your project.

4. Reference the Class Library in Other Projects


• To use the library in another project, right-click on the References or
Dependencies
node in that project, select Add Reference, and browse to your .dll file, or
select the
project if it's in the same solution.
• Once added, you can use the classes from the library in the new project by
adding a
using directive.

5. Publish and Share the Library (Optional)


• You can publish your class library as a NuGet package for easy distribution
and reuse.
• Right-click the project, select Pack, and follow the prompts to create
Building Class Libraries

Benefits of Using Class Libraries


• Code Reusability: Write once, use in multiple projects.

• Separation of Concerns: Keep related logic in one place, improving


organization.

• Easier Maintenance: Update functionality in one location without changing


every project.

Class libraries are powerful for modularizing code, enhancing maintainability,


and creating
reusable components for applications in C#.
03
Storing Data
with Fields
Storing Data with Fields
In C#, fields are used to store data within a class. Fields are
variables defined directly in a class, allowing objects of that
class to hold their own unique data. Fields help maintain state
and store values that are needed by the class's methods.

Declaring Fields
A field is declared within a class but outside any methods,
constructors, or properties. Fields are typically private to
encapsulate the class's data, but they can be made public or
protected as needed.

Here, name and age are fields that


will store date specific to each
Person object.
04
Writing and
Calling with
Methods
Writing and Calling
Methods
In object-oriented programming (OOP), writing and calling methods in C# involves
creating
functions within classes to encapsulate code, which you can then use to perform
tasks and
1. Writing Methods
organize logic.
Writing a method means defining it within a class. Methods typically consist of an
access
modifier, a return type, a name, and optional parameters.
Explanation:
• public : Access modifier (can be public , private ,
protected , etc.), which defines visibility.
• int : Return type, specifying the type of value the
method returns.
• Add : Method name, used to identify and call the
method.
• (int a, int b) : Parameters, representing input
values for the method.
• return a + b; : Statement that gives back a value
of a + b to wherever the method is called.
Writing and Calling
1. Calling Methods
Methods
Calling a method means using it in code to perform an action or return a value. To
call a method, you first need to create an instance of the class (if it's not
static ) and then invoke the method using the instance.
Explanation:
• myclass myclasslnstance = new myclass(); :
Creates an instance of Myclass .
• myclassInstance.Add(5, 10); : Calls the Add
method with 5 and 10 as arguments.
• Console .writeLine( ... ) : Outputs the result.
Key Points:
• Instance Methods: Require creating an
instance of the class (e.g., Myclass
myclasslnstance = new Myclass();
• Static Methods: Can be called directly on the
class without creating an instance (e.g.,
math.Sqrt(25);
• Return Type: If void , the method doesn't
returnand
This process allows you to write reusable, organized, a value, just performs an action.
easily understandable
05
Controlling
Access with
Properties and
Controlling Access with
Properties and Indexers
In C#, properties and indexers provide ways to control access to class data more
securely and flexibly than using fields directly. Properties allow controlled
access to a class’s fields, and indexers let you access data in an object similar
to how you would in an array, using indices.

1. Properties
Properties are class members that provide a flexible mechanism for reading,
writing, or computing the values of private fields. Properties allow you to
control access, perform validation, or even compute values on-the-fly, making
them ideal for encapsulating class data.

Basic Property Syntax: A property has get and set accessors:


• get : Retrieves (reads) the value of the property.
• set : Assigns (writes) a value to the property.
Controlling Access with
Properties and Indexers
Example:

Explanation: The Name


property controls access to
the name field, ensuring
that it's not set to a null or
empty value.
Auto-Implemented Properties
Auto-implemented properties simplify property declaration when no additional
logic is needed. The compiler automatically creates a backing field for the
property.

Example:
Read-Only and Write-Only
Properties
Properties can also be read-only or write-only, depending on whether they include
only a get
or set accessor.

Example of a Read-Only
Property:

• Explanation: The Area


property has only a get
accessor, so its value can
be read but not assigned
directly.
Properties with Private Setters
A common pattern is to make the set accessor private, allowing the property to be
modified
only within the class itself.

Example:
Indexers
Indexers allow instances of a class to be indexed just like arrays. They are useful
for classes that represent collections or have data that can logically be accessed
via an index.

Syntax of an Indexer: An indexer is defined with the this keyword, which allows
instances of
the class to be indexed.
Indexers
Example:
Combining Properties and
Indexers
Properties and indexers provide safe, controlled access to data within objects:
•Properties control individual values with potential validation.

• Indexers provide array-like access to classes, which is helpful for collection-like


data
structures.

These features help encapsulate data, enforce business rules, and allow for the
logical
structuring of how data is accessed and modified.
06
Pattern Matching
with Objects
Pattern Matching with
Pattern matching in C# allowsObjects
you to inspect an object's type and structure
directly in
expressions, simplifying conditional code by matching objects to patterns.
Introduced in C# 7 and expanded in later versions, pattern matching is
especially powerful with objects, enabling concise code for checking types,
properties, or specific values within complex conditions.
Types of Pattern Matching with
1. Pattern Matching with is. Objects
2. Property Pattern Matching.
3. Positional Pattern Matching.
4. when Clause in Pattern Matching
5. Combining Patterns
1. Pattern Matching with is.
The is pattern matches the type of an object, simplifying the as cast followed by a
null check.
This helps when you need to determine if an object is of a certain type and then
Example:
work with it.

• Explanation: Each is clause


both checks the type and
casts the object ( Circle c
and Rectangle r), allowing
immediate use of c or r
without further casting.
2. Property Pattern Matching
Property patterns allow you to match on an object's properties, making it easier to
express
complex conditions based on property values.
Example:

• Explanation: The { Age: >=


18 } pattern checks if the
person object has an Age
property that meets the
condition, categorizing the
person as an "Adult" or
"Child" based on age.
3. Positional Pattern Matching
Positional patterns are useful for objects with deconstruct methods or tuple-like
data. They
allow matching based on an object's positionally structured data rather than
named properties.
Example:

• Explanation: The Point class


has a deconstructor, so the
switch expression can use
positional matching on
point . Patterns like (o, o)
match the origin, and (o, _ )
matches points on the Y-
axis.
4. when Clause in Pattern
Matching.
when clauses allow additional conditions within a pattern match, providing greater
flexibility.

Example:

• Explanation: This switch


statement uses property
patterns and when
conditions. The when
condition refines the match
for circles with a specific
Radius and distinguishes
squares by checking width
== Height
5. Combining Patterns.
Patterns can be combined using or ( I ), and ( & ), and negation ( not ) to make
complex
matching scenarios.

Example:
Summary

Pattern matching with objects in C# simplifies conditional logic and enhances


readability. By
combining type, property, and positional matching with when clauses and logical
operators,
you can efficiently and expressively handle complex scenarios without multiple
casts and nested conditions.
07
Working with
Records
Working with Records
In C#, records are a special type of reference type designed for storing
immutable data with
built-in value-based equality, making them ideal for scenarios where data
immutability and
structural equality are important, such as data transfer objects or
configuration data. Records simplify code by automatically generating much
of the boilerplate code for immutable classes, such as equality checks and
Tostring methods. Defining Records
Records can be defined using either positional syntax or standard syntax with
properties,
depending on the level of control you need over the properties.
Positional Records
Positional records are concise and suited for simple data models where each
property should be immutable and doesn't require custom logic.

Example:

In this example:
• The Person record has two properties, FirstName
and LastName .
• Person automatically includes value-based
equality, Tostring, and GetHashcode methods
based on the property values.
Using a Positional Records

Here, person1 and person2 are considered equal because they


have the same property values, showcasing value-based
equality.
Standard Records with Property
Syntax
If you need more control or additional logic for each property, you can define
records using
standard property syntax. This approach is also helpful if you want some
properties to be
Example:
computed or if you need customization in the constructor.

init Keyword: The init keyword


makes properties assignable only
during object initialization,
preserving immutability.
Working with Immutability
Records are immutable by default. However, C# provides mechanisms like the
with expression to create modified copies of records without altering the
original.
Example Using with with
Expression:

• Explanation: The with expression creates a new Person record, copying all
values from
originalperson except for LastName , which is set to "Smith".
Equality and Comparison
Records use value-based equality, meaning two records are considered equal
if their property values are the same, unlike regular classes, which compare
by reference.
Example:

In this case, personA and personB are equal because their FirstName and
LastName values are identical, even though they are two different instances.
Inheritance with Records
Records support inheritance, and derived records also maintain immutability
and value-based equality by default.

Example:

In this setup:
• Animal is the base record with Name and Age properties.
• Dog inherits from Animal and adds a Breed property.

This setup allows Dog to be treated as an Animal , and it automatically


includes all the
properties of Animal for equality checks, Tostring , and other record
functionality.
Summary

Records in C# provide a clean, concise way to define immutable, value-based


objects. With
positional and standard property syntax options, as well as features like the with
expression
and built-in equality, records make it easy to work with data-centric classes where
immutability, structural equality, and readability are key.
08
Setting up a Class
Library and Console
Application
Setting up a Class Library and
Console
Setting up a class Application
library and a console application in C# involves creating
two separate projects in a solution: one for the class library (where reusable
code is stored) and another for the console application (which will use the
library).
Here’s a step-by-step guide to setting
up a class library and a console
application in Visual
Step 1: Create Studio:
a Solution and Class
Library
1. Open Visual Studio and go Project
to File > New > Project.
2. Select Class Library from the list (choose .NET Core or .NET Standard if you
want it to be
compatible across different platforms).
3. Name your library project (e.g., MyLibrary ), choose a location, and click
Create.
4. Visual Studio will create a solution with a Class Library project and a default
class file
(e.g., Class1.cs ).
Example Code for Class Library
(MyLibrary):

This Mathoperations class contains


two simple methods: Add and
multiply . This is the code that
will be reused in other
applications.
Step 2: Create a Console Application
Project
1. In Solution Explorer, right-click on the solution name (e.g., Mysolution ) and
select Add > New Project.
2. Choose Console Application (.NET Core or .NET Framework) and name it (e.g.,
MyConsoleApp ).
3. Click Create to add the console project to the solution.
Step 3: Add a Reference to the Class Library in the
Console
1. In Solution Explorer, right-click Application
on the References node under the
mycons01eApp project
and select Add Reference.
2. In the Reference Manager window, select Projects > Solution and check the
box next to
MyLibrary (the class library project).
3. Click 0K to add the reference. Now, Mycons01eApp can use the classes and
Step 4: Use the Class Library in the Console Application
methods
defined
1. Open in MyLibrary
the . file in the console application project ( myconsleeApp ).
Program.cs
2. Add a using directive for the MyLibrary namespace.
3. Create an instance of the class in MyLibrary and call its methods.
Example Code for Console Application
(MyConsoleApp):
09
Raising and
Handling Events
Raising and Handling Events
Raising and handling events in C# allows classes to notify other classes or
components when something interesting happens. This is especially useful for
decoupling components, as it allows one part of a program to react to
changes or actions in another part without being tightly linked to it.

Key Concepts for Events in C#


1. Events: Events are based on delegates, and they define notifications that
can be sent by a
class when certain actions occur.

2. Delegates: Delegates are types that define the signature of the method
that can be called
by the event.
Step 5: Build and Run the Solution
1. Set myconsoleApp as the Startup Project. Right-click on MyconsoleApp in
Solution Explorer
and select Set as Startup Project.
2. Press F5 or Ctrl + F5 to build and run the console application.

Summary
By setting up the class library and referencing it in the console application:

• You've created reusable code in MyLibrary.


• You've accessed this code from MyconsoleApp without duplicating it.

This setup is useful for modularizing functionality, testing individual


components, and reusing code across multiple applications.
Here's a step-by-step guide to
creating, raising, and handling events.
Step 1: Define a
Delegate
A delegate specifies the signature of the event handler method. C# provides a
built-in
EventHandler delegate that can be used for simple events without custom
arguments. If you need to pass specific data to the event handler, you can
create a custom delegate or use the EventHandler<T> delegate with a
custom EventArgs class.
Step 2: Define an Event in
a Class
Events are declared in a class and use the event keyword with a delegate
type. The class that owns the event will raise it when a specific action occurs.
Step 3: Raise the
Event
To raise the event, invoke it using the ?. Invoke syntax, which ensures that the
event has
subscribers before calling them.

Step 4: Handle the Event in


Another
To handle the event, Classclass subscribes to it by providing a method
an external
that matches the event delegate's signature. This method will be called
whenever the event is raised.
Example: Raising and
Here's a Handling
simple examplean Event
where a clock class raises an event whenever a
second ticks, and a
Display class handles it to show the current time.
Step 1: Define a Custom EventArgs
Class
If you need to pass (Optional)
data with the event, create a custom EventArgs class. In
this example, we’ll pass the current time.
Step 2: Define the Event in a
Publisher Class

The clock class will contain an event named


Tick, raised every second.
Step 3: Create a Subscriber Class to
The Display class Handle thetoEvent
will subscribe the Tick event and display
the time each time the event is raised.
Step 4: Subscribe to and
In the MainHandle the an
method, create Event
instance of clock , subscribe the
ShowTime method of Display to the Tick event, and start the
clock.
Explanation of How
This Works
• Event Declaration: The Tick event is
declared in clock using
EventHandler<TickEventArgs>.

• Raising the Event: OnTick is called every


second, which raises the Tick event and passes
the current time.

• Event Subscription: In Program.Main ,


clock.Tick display. ShowTime subscribes
Display. ShowTime to handle Tick events.

• Event Handling: ShowTime is called each


time the Tick event is raised, displaying the
current time.
Event Best
Practices
1. Check for Subscribers: Always check for null before raising events
(e.g., Tick?. Invoke(... ) ) to avoid NullReferenceException.

2. Use Protected Virtual Methods to Raise Events: Use protected


virtual methods (like OnTick ) for raising events, allowing derived
classes to override and extend event behavior.

3. Unsubscribe from Events: Unsubscribe from events when no longer


needed to prevent memory leaks, especially in long-running applications
(e.g., clock. Tick display. ShowTime; ).
10
Making Types
Safely Reusable
with Generics
Making Types Safely Reusable with
Generics in C# allow you toGenerics
create type-safe and reusable classes, methods,
and interfaces. By using generics, you can write flexible and reusable code
that can handle various data types while preserving type safety This is
particularly useful for collection classes, algorithms, and libraries where the
type of objects you're working with may vary.

Key Benefits of Generics


1. Type Safety: Generics prevent runtime type errors by enforcing type
constraints at compile time.

2. Code Reusability: Generics allow you to write code that works with any
data type, reducing redundancy.

3. Performance: Generics avoid boxing and unboxing operations that happen


when using
object types for collections.
Basic Syntax of
Generics
You define a generic by placing type parameters in angle brackets (T) after the
type name or method name. The T stands for Type and is a placeholder for any
type you want to use.

In this
example:
• GenericClass<T> is a generic class with a
type parameter T .

• T can be any type, allowing the class to store


and retrieve data of any type in a type-safe
manner.
Example: Generic
Here's a List
customClass
generic list class,
MyList<T> , which mimics the
behavior of List<T> but
provides a simple example of generic
collections.
Basic Syntax of
Generics
You define a generic by placing type parameters in angle brackets ( ) after the
type name or method name. The T stands for Type and is a placeholder for any
type you want to use.

In this
• example:
MyList<T> can store any type
(e.g., int, string) in a type-safe way.

• The list dynamically resizes itself


when full, and provides type-specific
access without
casting.
Generic
YouMethods
can also use generics with individual methods, enabling you to define
methods that can
operate on various types without needing to create a generic class.
Example of a Generic
Method
Usag
e:

In this
example:
• The PrintArray method works with any array type ( int[] ,
string[] , etc.) while
maintaining type safety.

• The <T> type parameter allows PrintArray to work with any


type.
Constraints in Generics
Constraints specify the requirements for the types that can be used with
generics, enabling
more control over generic types. For example, you might want to ensure that
a type parameter implements an interface or has a parameterless constructor.

Common Constraints
• where T : IComparable : Ensures T implements the IComparable
interface.

• where T : new() : Ensures T has a parameterless constructor.

• where T : class : Ensures T is a reference type.

• where T : struct : Ensures T is a value type.


Example with
Constraits:
Usage:
Summary

Generics make your code safer, more flexible, and reusable by allowing it to
work with any data type while enforcing type safety. Here's a quick summary
of their benefits:

• Type Safety: Avoids errors by enforcing type constraints at compile time.

• Code Reusability: Allows you to write code once and use it with various
data types.

• Performance: Avoids performance costs of boxing/unboxing and casting,


especially in collections.

Generics are widely used in the .NET library (e.g., List<T> ,


Dictionary<TKey, TValue> ) and are a powerful tool for building robust,
type-safe applications in C#.
11
Implementing
Interface
Implementing Interfaces
Implementing interfaces in C# allows you to define a contract that a class or
struct must follow. This contract specifies the methods, properties, events, or
indexers a type must implement, ensuring that it provides specific
functionality. Interfaces are a powerful tool for achieving abstraction,
polymorphism, and loose coupling in your code.
Key Concepts of Interfaces
1. Interface Definition: An interface is defined using the interface keyword
and typically only contains method, property, event, or indexer declarations
without implementations.

2. Implementing an Interface: A class or struct that implements an


interface must provide concrete implementations of all members defined by
that interface.

3. Polymorphism: Interfaces allow objects of different classes to be treated


as objects of the same type, enabling polymorphic behavior.

4. Multiple Inheritance of Interfaces: A class can implement multiple


interfaces, which provides flexibility in designing classes.
Example: Defining and Implementing an
Interface
Here's an example with a Vehicle interface that includes members for
starting, stopping, and getting the vehicle's speed. We'll then create two
classes, Car and Bicycle , that implement this a.

Step 1: Define the Interface

In this IVehicIe interface:


• Start and Stop are method declarations without implementations.
• Speed is a read-only property, which must be implemented by any class
that
implements IVehicIe .
Step 2: Implement the Interface
Now, let's createin
twoClasses
classes, Car and
Bicycle, that implement the IVehicIe
interface.

In these classes:

• Both Car and Bicycle implement


IVehicIe by providing concrete
implementations of Start, Stop, and
Speed.

• Each class sets a different initial


speed, simulating the behavior of
different
types of vehicles.
Step 3: Using Interface
You can usePolymorphism
IVehicle as a type to
create collections of different vehicle
types,
demonstrating polymorphism.

In this example:

• vehicles is a list of IVehicle


objects, which can contain both Car
and Bicycle instances.

• Each vehicle starts, displays its


speed, and then stops, demonstrating
polymorphic behavior.
Implementing Multiple Interface
In a class can implement multiple interfaces. For
example, if we define another interface,
IEIectricVehicIe , we can have Car implement
both IVehicIe and IElectricVehicle.

Now, ElectricCar can act as both an


IVehicle and an IElectricVehicle
combining the behavior of both
interfaces.
Explicit Interface Implementation
Sometimes, a class needs to implement
methods with the same names from
multiple interfaces. In these cases,
explicit interface implementation can
help disambiguate by associating each
method explicitly with an interface.
In this example:

• FlyingCar implements both


IFIyabIe and IDriveabIe , each with a
Move method.

• By using explicit interface


implementation
(void IFIyabIe.Move() ), you control
which method is called based on the
interface used to access the b .
Usage:
Summary

using interfaces in C#:

• Defines a contract that implementing classes must follow.

• Enables polymorphism by allowing objects of different classes to be used


interchangeably as the interface type.

• Supports multiple inheritance by letting a class implement multiple


interfaces. Interfaces are a fundamental part of building flexible, testable, and
loosely coupled applications in C#.
12
Managing Memory
with Reference and
Value Types
Managing Memory with Reference and
Value
In understanding the difference Types
between reference types and value types is
crucial for managing memory efficiently and writing performant code. Each
type behaves differently in terms of memory allocation, scope, and garbage
collection.
Value Types vs. Reference Types
1. Value Types: 2. Reference Types:

• Value types store data directly. • Reference types store a reference (or
• They are typically allocated on the pointer) to the actual data, not the data
stack (a region of memory that itself.
stores variables with a fixed scope). • They are allocated on the heap (a region
• Examples include primitive types of memory for dynamically allocated objects
like int, double, bool, struct, and with variable lifetimes).
enum . • Examples include classes, arrays, string,
• Value types have a fixed size, so delegates, and interfaces.
they are stored directly in memory • The heap is managed by the garbage
locations. collector, which frees memory that is no
Key Differences Between Value Types and
Reference Types
Assignment Behavior: Scope and Lifetime:
• For value types, when you assign • Value types are destroyed when they
one variable to another, a copy of the go out of scope (e.g., after a method
value is made. completes).
• For reference types, when you
assign one variable to another, only • Reference types stay in memory
the reference (pointer) is copied, so until the garbage collector determines
both variables point to the same that they are no longer in use.
memory location.

Memory Allocation:
• Value types are stored in stack
memory and have a smaller memory
footprint, making them quick to
allocate and deallocate.
• Reference types are stored in heap
memory, which is slower to allocate
and deallocate. However, they can
have complex structures and larger
Examples of Value Type and
Reference Type Behavior

• In the value type example,


changing b doesn't affect a as
each holds its own independent
value.

• In the reference type


example, person2 points to
the same memory as
person1 , so changes to
person2 affect person1 as
well.
Boxing and Unboxing
Boxing and unboxing are important concepts when working with value and
reference types:
• Boxing: Converting a value type to • Unboxing: Converting a boxed reference
a reference type by wrapping it in an type back to a value type. This requires an
object or interface type. This causes explicit cast and involves copying the value
a new object to be allocated on the from the heap back to the stack.
heap, and the value is copied into it.
Memory Management with Reference
Types (Garbage Collection)
The garbage collector (GC) in automatically manages memory for reference
types. It periodically checks the heap for objects that are no longer accessible
and deallocates their memory.

The GC has three main generations to


3. Generation 2. Long-lived objects (e.g.,
optimize performance:
static data or objects in application
lifetime). Collected least frequently.
1. Generation O: Short-lived objects
(e.g., local variables in methods).
The GC runs automatically, but you can also
Collected
trigger it manually using GC.CoIIect() .
most frequently.
However, this is generally discouraged, as it
disrupts the GC's
2. Generation 1: Objects that have
optimization and can reduce performance.
survived one GC cycle but are still in
use.
Collected less frequently.
Using ref and out Keywords
The ref and out keywords allow you to pass value types by reference, which
enables a method to modify the original variable rather than a copy.
2. ref keyword:
• Allows a variable to be passed by
reference.
• Requires that the variable is
initialized before it'S passed to the
method.

3. out keyword:
• Also allows a variable to be passed
by reference but does not require it to
be initialized beforehand.
• The method must assign a value to
the parameter before it returns.
Using ref and out Keywords
using ref and out is particularly helpful for modifying value types without
creating unnecessary copies, but it should be used sparingly as it can make
code harder to read.
Choosing Between Value Types and
Reference Types
• Use value types for small, simple • Use reference types for more complex
data (e.g., numbers, structs) where data structures that need to be shared
performance is critical. across multiple locations in the program or
may grow in size.

Understanding these principles helps you


control memory usage more effectively,
optimize performance, and reduce the risk
of memory leaks in your applications.
13
Inheriting from
Classes
Inheriting from Class
Inheriting from classes in is a key aspect of object-oriented programming
(OOP) that enables you to create a new class (the derived or child class)
based on an existing class (the base or parent class). Inheritance allows the
derived class to reuse the code and functionality of the base class while
adding or overriding behaviors as needed.
Basics of Inheritance
• The base class is the class from which properties, methods, and
other
members are inherited.

• The derived class inherits all accessible members (fields,


properties methods)
from the base class and can add new members or override existing
ones.

• In C# inheritance is single inheritance, meaning a class can


inherit from only
one base class directly, although it can implement multiple interfaces.
Syntax of Inheritance
use the : symbol to indicate that a class is inheriting from a base class.
Example: Inheriting a Class
Let's consider a real-world example with an Animal base class and a Dog derived
class.

• The Dog class inherits properties


and methods from the Animal class,
so Dog instances have access to
Name and Eat.

• MakeSound is a virtual method


in the Animal class and is
overridden in the Dog class to
customize behavior for dogs.
Usage Example

In this example:
• myDog can call Eat (from Animal), • This setup illustrates polymorphism,
Bark (specific to Dog) and the where a derived class can change or
overridden Makesound method. extend the behavior of the base
class.
Access Modifiers and Inheritance

Access modifiers control the visibility of members in inheritance:

• public : Accessible everywhere.

• protected : Accessible within the class and derived classes.

• private : Accessible only within the class (not inherited).

• internal : Accessible within the same assembly.


In most inheritance scenarios, protected is used for members meant to be
accessible by derived classes but hidden from other code.
Constructors in Inheritance
The base class constructor is called
first when a derived class is
instantiated. If a base class has a
parameterized constructor, you can
use the base keyword to call it from
the derived class constructor.

Usage:
Abstract Classes and Methods
Abstract classes serve as templates
and cannot be instantiated. They may
include abstract methods, which have
no implementation in the base class
and must be overridden in any
derived class.

Usage:
Sealed Classes and Methods
The sealed keyword prevents a class or method from being further inherited
or overridden.
1. Sealed Class: Prevents the class from being used as a
base class.

2. Sealed Method: A method in a


derived class can be marked as
sealed to prevent further overriding.

Usage:

Here, Makesound cannot be


overridden further in any class that
might try to inherit from Dog.
Summary of Key Points
• Inheritance promotes code reuse by allowing a derived class to
inherit members from a base class.

• Access Modifiers control which members are inherited and


accessible in the derived class.

• Constructors: Use base to call the base class constructor when


needed.

• Virtual and Override allow you to define and modify behavior in


derived classes.

• Abstract Classes and Methods are templates that must be


completed by derived classes.

• Sealed prevents further inheritance or method overriding.

By understanding and using inheritance, you can create a structured,


maintainable, and efficient object-oriented codebase.
14
Working with Null
Values
Working with Null Values
In managing null values effectively is crucial for writing safe, error-free code.
Null values are often used to indicate the absence of a value, but improper
handling can lead to QIReferenceException errors, which occur when you
attempt to use an object that hasn't been initialized. offers various ways to
handle null values safely.
1. Nullable Reference Types
In 8.0 and later, nullable reference types were introduced to help
developers avoid null-related errors. By default, reference types
cannot be null unless explicitly marked as nullable.

To enable nullable reference types in a project, you can add the


following line in the project file or settings:
Nullable Reference Type Example
With nullable reference types enabled, you can explicitly declare that a
reference
type may contain a null value using the symbol:

Here:
• Name cannot be null, so assigning null to
it will produce a waming.

• MiddleName is marked as nullable,


meaning it can hold either a string or null.
2. Nullable Value Types
C# value types (like int, double, bool) cannot be null by default, but
you can
make them nullable using ?. This is particularly useful for indicating
the absence
of a value, like a database field that may or may not contain data.

This is represented as Nullable<int> under the hood, which has


properties like .HasValue (to check if a value is present) and .Value
(to access the actual value).
3. Null Conditional Operator ( ?. )
The null conditional operator (?.) allows you to safely access
members of an object without triggering a NullReferenceException.
Ifthe object is null , it will short-circuit and return null.

Here, person?.Name will return null if person is null , instead of


attempting to access Name and causing an error.
4. Null Coalescing Operator ( ?? )
The null coalescing operator (??) provides a fallback value if the left
operand is
null . This is useful for setting defaults or ensuring non-null return
values.

Example:

In this case, displayName will hold "Guest" if name is null.


5. Null Coalescing Assignment Operator (
The null coalescing assignment operator (??= ) allows you to assign a
value to a variable only if it’s currently null.

Example:

This is particularly helpful for initializing values that may not yet be
set.
6. NullReferenceException Handling
NullReferenceException is a runtime error that occurs when you
attempt to
access a member of a null object. You can prevent it by:
• using null checks before accessing members.
• using nullable types and the null conditional operator to handle
potential
null values safely.

Example:
7. Null Checking with is and switch Statements
You can use pattern matching with is or switch expressions for null
checking.

Example with is :

Example with switch expression :


8. Working with Null in Collections
Nullable collections, such as lists of nullable types ( or List<string?
> ), can be List<string?> helpful when working with data where
some elements may be missing.

Example:
9. Null Forgiving Operator ( ! )
The null-forgiving operator (!) tells the compiler that a nullable
variable will not
be null at runtime, preventing warnings about nullability. Use it with
caution as it
suppresses compiler warnings but does not provide runtime safety.

Example:

This should be used sparingly, typically when you have external logic
ensuring a variable isn't null but the compiler doesn't recognize it.
Summary of Null-Handling Techniques

By effectively handling null


values, you can avoid common
pitfalls, write safer code, and
ensure that your applications
are more robust and less prone
to runtime errors.
15
Casting within
Inheriting
Hierarchies
Casting within Inheriting Hierarchies

Casting within inheritance hierarchies in C# allows you to treat an object as an


instance of a different type within the same hierarchy. This can be helpful for
accessing base class members, invoking overridden methods, or utilizing
polymorphism. There are two main types of casting in C#. implicit and
explicit. Additionally, type-checking operators such as is , as and pattern
matching are useful for safe and efficient casting.
1. Implicit Casting (Upcasting)
Upcasting is the process of converting a derived class instance to a
base class type. This type of casting is implicit in C# because every
derived class is also an instance of its base class. With upcasting, you
can work with an object using the base class reference, allowing for
polymorphism.
Exampl
e:

Here:

• myDog is implicitly cast to


myAnimal , an Animal reference.
Since myAnimal is
treated as an Animal you can only
access members defined in Animal.
• Bark() isn't accessible because
myAnimal is treated as a base class
reference.
2. Explicit Casting (Downcasting)
Downcasting is the process of converting a base class reference
back to a derived
class type. This cast must be explicit because not all base class
instances are derived class instances. If the instance being
downcasted isn't actually of the derived type, a
InvalidCastException
Exampl will occur.
e:

Here:
• The myAnimal object is downcast to a Dog
reference, allowing access to Bark().

• Caution: If myAnimal were an instance of a


different derived type (e.g., Cat) this cast would
throw an exception at runtime.
3. Safe Casting with is and as
using the is and as operators lets you safely check and perform casts
without
risking exceptions.

• is Operator. Checks if an object is of a specific type, returning true


if it is
and false otherwise.
• as Operator: Tries to cast an object to a specified type, returning
Example
null if thewith is
cast fails.

• This example uses


pattern matching
with is to declare and
initialize myDog only if
myAnimaI is actually a
Dog .
Example with as
4. Casting with Pattem Matching in switch
Pattern matching in switch statements provides a more concise way to
check an
object's type and cast it.
Exampl
e:

In this example:

• myAnimaI is checked against


specific types (Dog and Cat) within
the switch
statement.

• Each case automatically casts


myAnimal to the matched type (e.g.,
Dog or Cat).
5. Polymorphism with Casting
Polymorphism allows you to invoke derived class methods through
base class references. This is particularly useful when a derived class
overrides a base class method.
Exampl
e:

Here:

• myAnimal.MakeSound() calls Dog


‘s MakeSound() method due to
polymorphism, even though
myAnima1 is an Animal reference.

• Polymorphism works when the


method in the base class is marked as
virtual and is overridden in the
derived class.
Summary of Casting Techniques

Effective casting within


inheritance hierarchies,
combined with polymorphism
and type-checking, can make
code more flexible and robust.
By using these techniques, you
ensure that your code is safer
and performs correctly without
risking runtime errors from
invalid casts.
16
Inheriting and
Extending .NET
types
Inheriting and Extending .NET types
Inheriting and extending .NET types allows you to build on existing classes and
structures provided by the .NET Framework, enabling you to add custom
functionality while leveraging the robustness of .NETs built-in types. This is
commonly done by extending classes such as List , Dictionary , Exception ,
and others. However, some types, like string and other sealed classes,
cannot be directly inherited. In those cases, we can extend functionality
through extension methods.
1. Inheriting from .NET Classes
Inheriting from .NET classes is similar to inheriting from custom classes, but
with a
few considerations:

• Not all .NET types can be inherited (e.g., string, DateTime , and Math are
led).

• Use inheritance when you need to modify behavior or add functionality that
fits naturally within the type's purpose.
Example: Custom List Class
Let's extend the List<T> class to create a LimetedList<T> that
enforces a maximum capacity.

Usage:

Here:

• We use new to hide the original Add


method, which allows us to add capacity
checking.

• base.Add(item) calls the original Add


method from List<T> if the item count is
below the capacity.
2. Extending with Custom Exceptions
Custom exceptions let you provide more context when handling
specific errors. By inheriting from Exception , you can create a
specialized exception type.

Usage in
LimitedList:

This approach allows more robust error


handling by using try-catch with a specific
exception type.
3. Extending Functionality of Sealed Classes with Extension
Methods
Some .NET types, like string and DateTime are sealed , so they
can't be inherited directly. Instead, you can use extension methods to
add custom functionality.

Example: Extension Method for string


Usage:
Let's create an extension method to count vowels in a string.

Here:

• CountVowels is an extension method


for string , accessible as though it’s a
member of string.

• The this keyword before the first


parameter string str makes it an
extension method for string.
4. Extending Collection Classes
The .NET System.collections.Generic namespace provides several
types like Dictionary, Queue, and Stack . You can inherit from these
or create custom wrappers for specialized behavior.
Example: Custom Dictionary with Default Value
Let's create a DefaultDictionary<TKey, TValue> that returns a
default value for keys that aren't present.

Usage:
Summary of Extending .NET Types

Inheriting from and


extending .NET types helps create
customized solutions tailored to
specific application requirements,
while extension methods and
abstract base classes provide
structure and flexibility without
needing to rewrite core .NET
functionality.
THANK YOU!!

You might also like