OOPS
OOPS:
Object-Oriented Programming (OOP) is a programming paradigm
that structures software design around objects, which encapsulate both data and
methods. In C#, OOP facilitates the creation of modular, reusable, and
maintainable code by modeling real-world entities and their behaviors.
By organizing code into classes and objects, OOP leverages
key principles such as encapsulation, inheritance, polymorphism, and
abstraction to enhance flexibility, reduce redundancy, and improve code
management.
OOP offers several advantages:
It provides a clear structure for programs.
It adheres to the DRY (Don't Repeat Yourself) principle,
making code easier to maintain, modify, and debug.
It enables the creation of reusable applications with less
code and shorter development times.
In OOP, a class serves as a template for creating objects,
while an object is an instance of a class. When objects are created, they
inherit all the variables and methods defined in their class.
Class:
A class defines the structure of an object, including
variables (or attributes) for storing data and methods (or functions) to
perform operations on that data. It is a fundamental building block in
object-oriented programming (OOP), providing a template for creating objects
and encapsulating both data and behavior. By using classes, developers can
create well-structured, maintainable, and reusable code.
Object:
In C#, an object is an instance of a class, created using
the `new` keyword.
It represents the class's structure, including its data and
methods. Each object has a unique state and behavior, with its own fields and
methods as defined by the class. The `new` keyword allocates memory for the
object and returns a reference that allows interaction with its data and
methods.
Constructors in C#
A constructor is a special method in a class that is
automatically invoked when an instance of the class is created. Its primary
purpose is to initialize the object's data and set up any necessary state
before the object is used.
Key points about constructors:
A constructor has the same name as the class and does not
have a return type, not even void.
Constructors are used to set initial values for the object’s
fields or to perform any setup operations needed before the object is used.
Constructors are invoked during object creation, allowing
for proper initialization of the object.
Multiple constructors can be defined within a class, each
with different parameters. This feature, known as constructor overloading,
provides flexibility in object creation.
Types of Constructors:
Default Constructor: A parameterless constructor provided automatically by C# if no constructors are explicitly defined in the class. It initializes the object with default values. If you define any other constructors, the default constructor is no longer provided automatically.
Parameterized Constructor: A constructor that accepts
parameters, allowing for customized initialization values when creating an
object. This enables more control over the initial state of an object.
Static Constructor: A constructor used to initialize static
members of the class. It cannot take parameters and is called automatically
when the class is first accessed. This constructor is useful for setting up any
static data or performing actions that need to occur only once per class,
rather than per instance.
Primary Constructor (Introduced in C# 9.0):
A streamlined syntax for defining constructors that
initializes properties directly within the constructor's parameters. This
feature reduces boilerplate code by combining property declarations and
initialization in a single statement.
Benefits: Primary constructors simplify object creation by
reducing redundancy, especially for classes with simple initialization needs.
Primary Constructor Example:
public class Person(string name, int age)
{
public string Name
{ get; } = name;
public int Age {
get; } = age;
}
Access modifiers:
Access modifiers in C# are used to control the visibility
and accessibility of classes, methods, and other members. They play a crucial
role in encapsulating data and protecting the integrity of an object’s state.
public:
Purpose: Allows unrestricted access to the class members from any part of the program, including other classes and assemblies.
Use Case: Use public for members that need to be accessible
by other classes, such as methods or properties that provide essential
functionality or data.
Example
public class Person
{
public string Name
{ get; set; }
public void
Introduce()
{
Console.WriteLine($"Hello, my name is {Name}.");
}
}
private:
Purpose: Restricts access to the class members so they can
only be accessed within the same class. This ensures that internal data and
operations are hidden from outside interference.
Use Case: Use private for internal data and helper methods
that should not be exposed to other classes. It helps maintain the integrity of
the object by controlling how its data is accessed and modified.
Example:
public class BankAccount
{
private decimal
balance;
public void
Deposit(decimal amount)
{
if (amount
> 0)
balance +=
amount;
}
public decimal
GetBalance()
{
return
balance;
}
}
protected:
Purpose: Allows access to members from within the same class
and from derived classes. It’s useful for creating class hierarchies where
child classes need to access or modify base class members.
Use Case: Use protected when you want to expose data or
methods to subclasses but not to other classes or external code.
Example:
public class Animal
{
protected string
species;
public void
SetSpecies(string species)
{
this.species =
species;
}
}
public class Dog : Animal
{
public void
DisplaySpecies()
{
Console.WriteLine($"This
is a {species}."); // Accessible due to protected
}
}
internal:
Purpose: Restricts access to members so they can only be
accessed within the same assembly. This is useful for hiding implementation
details from other assemblies while exposing them within the assembly.
Use Case: Use internal for classes and members that are
intended to be used only within the same project or library.
Example:
internal class InternalHelper
{
internal void
Help()
{
Console.WriteLine("Helping with internal tasks.");
}
}
protected internal:
Purpose: Combines protected and internal access levels,
allowing access within the same assembly and in derived classes from any
assembly.
Use Case: Use protected internal when you want to allow
access to derived classes and classes within the same assembly, but not from
external assemblies.
Example:
public class BaseClass
{
protected internal
string data;
public BaseClass()
{
data =
"Accessible within the same assembly or by derived classes.";
}
}
public class DerivedClass : BaseClass
{
public void
ShowData()
{
Console.WriteLine(data); // Accessible due to protected internal
}
}
private protected:
Purpose: Restricts access to members so they are only
accessible within the containing class and by derived classes in the same
assembly. This is a more restrictive combination of private and protected.
Use Case: Use private protected to limit access to derived
classes within the same assembly, providing a high level of encapsulation while
allowing some flexibility for inheritance.
Example:
public class BaseClass
{
private protected
string restrictedData;
public BaseClass()
{
restrictedData
= "Accessible only within this assembly and by derived classes.";
}
}
public class DerivedClass : BaseClass
{
public void
ShowRestrictedData()
{
Console.WriteLine(restrictedData); // Accessible due to private
protected
}
}
Access modifiers in C# are essential for controlling how and
where class members can be accessed. They help maintain encapsulation by
defining clear boundaries around data and functionality. By choosing the
appropriate access modifier, you ensure that your code remains secure,
maintainable, and understandable.
Properties in C#:
In C#, properties are members of a class or struct that
offer a controlled mechanism to read, write, or compute the values of private
fields. They combine the simplicity of fields with the flexibility of methods,
allowing data to be exposed and managed securely and efficiently. By using
properties, you can encapsulate data, enforce validation, and enhance
maintainability and security, leading to a more robust object-oriented design.
Components of a Property
1. Accessor Methods:
Getter (get): Retrieves the value of the property.
Setter (set): Assigns a value to the property.
public class Person
{
private string
name; // Private field
public string
Name // Property
{
get { return
name; } // Getter
set { name =
value; } // Setter
}
}
2. Auto-Implemented Properties:
These simplify property declaration by eliminating the need
for a private field. The compiler automatically creates a backing field.
Example:
public class Person
{
public string Name
{ get; set; } // Auto-implemented
property
public int Age {
get; set; } // Auto-implemented
property
}
3. Read-Only and Write-Only Properties:
Read-Only: Only has a getter and cannot be set from outside
the class.
Write-Only: Only has a setter and cannot be read from
outside the class.
Examples:
public class Person
{
private string
name;
private int
birthYear;
// Read-only
property
public string Name
{
get { return
name; }
}
// Write-only
property
public int
BirthYear
{
set {
birthYear = value; }
}
// Constructor to
set Name
public
Person(string name)
{
this.name =
name;
}
}
4. Expression-bodied Properties:
A shorthand syntax for properties with a single expression
in the getter or setter.
Example:
public class Circle
{
private double
radius;
public double
Radius
{
get =>
radius; // Expression-bodied getter
set =>
radius = value; // Expression-bodied
setter
}
public double
Diameter => 2 * radius; // Read-only
property with expression-bodied getter
}
5. Property with Logic:
Properties can include logic within the getter and setter to
enforce constraints or perform additional operations.
public class BankAccount
{
private decimal
balance;
public decimal
Balance
{
get { return
balance; }
set
{
if (value
< 0)
throw
new ArgumentException("Balance cannot be negative");
balance =
value;
}
}
}
6. Indexers:
Special properties that allow instances of a class to be
indexed like arrays. Indexers can have different access levels for getters and
setters.
Example:
public class PhoneBook
{
private
Dictionary<string, string> contacts = new Dictionary<string,
string>();
public string
this[string name]
{
get
{
if
(contacts.TryGetValue(name, out var number))
return
number;
return
"Contact not found";
}
set
{
contacts[name] = value;
}
}
}
Benefits of Using Properties
Encapsulation:
Properties allow for control over how data is accessed and
modified, providing a way to enforce validation rules and maintain
encapsulation.
Readability and Maintenance:
Properties improve code readability by providing a clear and
consistent way to access and modify class data. They also make it easier to
update the implementation later without changing the class's public interface.
Flexibility:
Properties can be used to compute values dynamically
(read-only properties) or to implement complex logic (getters and setters)
while maintaining a clean and intuitive syntax.
'as' Operator:
The as operator in C# provides a way to perform safe type
casting, which avoids exceptions and allows you to handle cases where the cast
might fail. It is particularly useful with reference types and nullable value
types.
The as operator is a handy tool for working with types
dynamically and helps in maintaining cleaner and safer code by avoiding
unnecessary exceptions.
Safe Casting: The as operator attempts to cast an object to
a specified type. If the cast is not possible, it returns null instead of
throwing an exception.
Reference Types and Nullable Types: The as operator works
with reference types and nullable types (e.g., int?). It does not work with
non-nullable value types directly. To use as with value types, you need to use
nullable types, like int?.
Avoiding Exceptions: Using as is preferable in situations
where you are unsure of the object's type and want to avoid the overhead of
handling exceptions like InvalidCastException.
Limitations
Non-nullable Value Types: You cannot use as to cast directly
to non-nullable value types like int or double. For such cases, you must use
explicit casting and handle possible exceptions or use the is keyword combined
with conditional logic.
Performance Considerations: While using as is generally
efficient, be mindful of performance impacts in critical code paths where type
checking might be frequent.
Examples
Casting to Reference Type:
object obj = "Hello, World!";
string str = obj as string;
// str is "Hello, World!" because obj is a string
Casting to Nullable Type:
object obj = 123;
int? number = obj as int?;
// number is 123, since obj can be cast to int?
Handling Failure:
object obj = 123;
string str = obj as string;
// str is null because obj is not a string
if (str == null)
{
Console.WriteLine("Cast failed.");
}
Example:
object obj = "Hello, World!";
string str = obj as string;
// Safe cast; str will be "Hello, World!"
Explanation:
The as operator helps preserve the original object’s type
and allows checking if the cast is possible without altering the original
object.
Encapsulation:
Encapsulation is the principle of bundling data (fields) and
methods (functions) into a single unit, known as a class, and restricting
direct access to some of its components. This is done using access modifiers to
control visibility and access levels.
Encapsulation is a fundamental concept in object-oriented
design that enhances code security, maintainability, and modularity. By hiding
the internal state of an object and requiring interaction through public
methods, encapsulation protects data integrity and provides a clear, controlled
interface for class interaction.
Encapsulation improves code modularity by grouping related
data and methods into a single class. This setup minimizes the impact of
changes, ensuring that modifications to one part of the class have little to no
effect on other parts of the program.
Encapsulation facilitates easier maintenance and updates by
isolating the internal implementation of a class. Changes to how a class works
internally do not affect other parts of the application that use the class,
reducing the risk of unintended side effects.
The access modifiers used to implement encapsulation are:
Private: Restricts access to members of the class. Only the
class itself can access its private fields and methods.
Public: Allows access to members from any part of the
program.
Protected: Allows access to members from within the class
and its derived classes.
Internal: Allows access to members within the same assembly.
Protected Internal: Allows access to members from within the
same assembly or from derived classes.
Comments
Post a Comment