Cprog 2
Cprog 2
Object-Oriented Programming (OOP) is a programming paradigm that is based on the concept of "objects", which can contain data
(in the form of attributes) and code (in the form of methods) that manipulate that data. The main concepts of OOP are:
Class: A class is a blueprint for creating objects that defines a set of attributes and methods that the objects will have. It represents a
collection of objects that have similar properties and behaviors.
Object: An object is an instance of a class. It is a self-contained entity that contains data and code to manipulate that data.
Encapsulation: Encapsulation is the practice of hiding the internal details of an object and only exposing the necessary information
through a well-defined interface. This ensures that the object's data is protected and can only be accessed through the methods defined
in the class.
Inheritance: Inheritance is the ability of a class to inherit properties and methods from its parent class. This allows for code reuse and
allows for the creation of more specialized classes based on existing classes.
Polymorphism: Polymorphism is the ability of an object to take on many forms. This allows for a single interface to be used to
represent multiple different types of objects.
Abstraction: Abstraction is the process of identifying essential features of an object and ignoring the non-essential details. This
allows for the creation of simpler and more modular code.
In summary, OOP is a programming paradigm that allows for the creation of complex, modular, and reusable code by
defining objects that encapsulate data and behavior, and using inheritance, polymorphism, and abstraction to build more specialized
classes and simplify code.
using System;
In this example, we define a class named Person that has two instance variables or fields: name and age. We also define a
constructor for the class that takes two parameters: name and age. Finally, we define a method named SayHello that displays a
message that includes the name and age of the person.
We then create an object named person1 of the Person class using the new keyword and passing in two arguments to the
constructor: "John" for the name and 30 for the age. Finally, we call the SayHello method of the person1 object, which displays
the message: "Hello, my name is John and I am 30 years old."
Class Members
Class members are the properties, methods, constructors, and events that are defined within a class.
Fields: Fields are variables that hold the state or data of an object. They represent the characteristics or attributes of an object.
Properties: Properties are used to provide controlled access to the fields of an object. They provide a way to read or write the values
of the fields.
Methods: Methods are used to define the behaviors or actions of an object. They are functions that can be called to perform some
operation or task.
Constructors: Constructors are special methods that are called when an object of a class is created. They are used to initialize the
fields of an object to default values.
Events: Events are used to provide a way to communicate between objects. They allow one object to notify other objects when
something important happens.
Here is an example of a class that defines fields, properties, methods, and constructors:
class Person
{
// Fields
private string name;
private int age;
// Properties
public string Name
{
get { return name; }
set { name = value; }
}
// Constructors
public Person()
{
Name = "";
Age = 0;
}
// Methods
public void SayHello()
{
Console.WriteLine("Hello, my name is " + Name + " and I am " + Age + " years old.");
}
}
In this example, the Person class has two private fields (name and age), two public properties (Name and Age), and two
constructors (one with no parameters and another with name and age parameters). It also has a method named SayHello that
writes a message to the console.
The Name and Age properties provide controlled access to the name and age fields. The constructors are used to initialize the fields
to default or user-specified values. The SayHello method is used to define the behavior of a person object when it is called.
Enums
An enumeration, or enum for short, is a set of named constant values that represent a range of values. In C#, enums are declared using
the enum keyword. Enums are useful when you want to represent a set of related constant values that have a specific order or
meaning.
enum DaysOfWeek
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
In this example, we define an enum named DaysOfWeek that represents the days of the week. Each of the seven days of the week is
represented by a named constant value. By default, the underlying type of an enum is int, but you can specify a different underlying
type if needed.
In this example, we create a variable named today of type DaysOfWeek and initialize it to the Monday value. We then use an if
statement to check whether today is equal to Saturday or Sunday, and if so, we write a message to the console saying it's the
weekend. Otherwise, we write a message saying it's a weekday.
Enums are useful when you want to represent a set of related values that have a specific order or meaning. They can make your code
easier to read and understand, and they can help catch errors at compile time.
Accessors
Accessors, also known as getters and setters, are methods used to control access to a private field in a class. They allow you to read or
write the value of a private field in a controlled way, by providing a layer of abstraction that separates the internal implementation of
the class from its external interface.
In C#, you can create accessors for a property by defining the get and/or set keywords. The get accessor is used to retrieve the
value of the property, while the set accessor is used to set the value of the property. Here's an example:
class Person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
In this example, we define a class called Person with a private field called name, and a public property called Name that provides
access to the name field. The get accessor returns the value of the name field, while the set accessor sets the value of the name
field.
You can also define an accessor without a corresponding set or get accessor. This is useful when you want to create a read-only or
write-only property. Here's an example:
class Circle
{
private double radius;
public double Area
{
get { return Math.PI * radius * radius; }
}
}
In this example, we define a class called Circle with a private field called radius, and a public read-only property called Area
that calculates the area of the circle using the radius field. Since there is no set accessor defined for Area, it is read-only.
Accessors are useful because they allow you to control how clients of your class can access its private fields. By providing a layer of
abstraction, you can change the internal implementation of the class without affecting the external interface. This can make your code
more modular and easier to maintain over time.
Access Modifiers
Access modifiers are keywords in C# that determine the accessibility of a class, method, field, or property from other parts of the
program. There are five access modifiers in C#:
public: A public member is accessible from any part of the program. It has the widest accessibility.
private: A private member is only accessible within the class where it is defined. It has the narrowest accessibility.
protected: A protected member is accessible within the class where it is defined and any derived classes.
internal: An internal member is accessible within the same assembly (i.e., a .dll or .exe file).
protected internal: A protected internal member is accessible within the same assembly and any derived classes.
In this example, we define a public class called Person with a private field called name, a public method called GetName() that
returns the value of the name field, and a protected method called SetName() that sets the value of the name field. The SetName()
method is protected because it can only be accessed within the Person class and any derived classes.
Access modifiers are important because they help enforce encapsulation and information hiding, which are key principles of object-
oriented programming. By restricting access to certain members of a class, you can prevent unintended modifications or access to
sensitive data. At the same time, by providing public access to some members, you can expose a well-defined interface to the rest of
the program, making it easier to use your classes and components.
Constructors are used to initialize the state of an object when it is first created. They have the same name as the class and do not have
a return type. Constructors can have parameters, which can be used to set the initial state of the object. If a constructor is not defined
explicitly, C# will provide a default constructor with no parameters.
class Person
{
private string name;
private int age;
In this example, we define a class called Person with two private fields, name and age. We also define a constructor that takes two
parameters, name and age, and sets the values of the corresponding fields. The this keyword is used to distinguish between the
class fields and the constructor parameters with the same name.
Destructors, on the other hand, are used to clean up the resources used by an object when it is no longer needed. They are called
automatically by the garbage collector when the object is being removed from memory. Destructors are defined with the same name as
the class, preceded by a tilde (~), and do not take any parameters.
class Person
{
private string name;
~Person()
{
Console.WriteLine("Person object destroyed");
}
}
In this example, we define a class called Person with a private field name and a destructor that writes a message to the console
when the object is destroyed.
It's important to note that C# does not support deterministic destruction of objects. This means that you cannot predict exactly when
the destructor will be called, and you should not rely on it for resource cleanup. Instead, it's recommended to use Dispose methods
and the using statement to manage resources explicitly.
Properties are used to encapsulate the state of an object. They provide a way to get and set the values of the private fields of a class,
while hiding the implementation details. Properties can be used to enforce business logic or validation rules, and they can be used to
provide a well-defined interface to the rest of the program.
class Person
{
private string name;
In this example, we define a class called Person with a private field name. We also define a public property called Name, which
provides access to the name field. The get accessor is used to retrieve the value of the field, while the set accessor is used to set
the value of the field. The value keyword is used to represent the value being set.
Methods, on the other hand, are used to encapsulate the behavior of an object. They provide a way to perform actions on the object or
to return values based on the object's state. Methods can be public or private, depending on whether they need to be accessible from
other parts of the program.
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
In this example, we define a class called Calculator with a public method called Add, which takes two integer parameters a and b
and returns their sum.
Both properties and methods can have access modifiers, such as public, private, protected, internal, and protected
internal, which determine their accessibility from other parts of the program. They can also have additional modifiers, such as
static, virtual, abstract, override, and sealed, which modify their behavior in various ways.
Types of Methods
There are several types of methods in C# that are used to encapsulate behavior in different ways. Here are some examples of each
type:
Instance methods - These methods are associated with a particular instance of a class and can access the instance's fields and
properties. Here's an example:
class Person
{
private string name;
private int age;
In this example, we define a class called Person with two private fields, name and age, and a public instance method called
PrintDetails, which prints the person's name and age to the console.
Static methods - These methods are associated with the class itself and do not require an instance to be created. They can only access
static fields and properties of the class. Here's an example:
class Calculator
{
public static int Add(int a, int b)
{
return a + b;
}
}
In this example, we define a class called Calculator with a public static method called Add, which takes two integer parameters a
and b and returns their sum.
Virtual methods - These methods can be overridden by derived classes. They are used to define a default implementation that can be
customized by subclasses. Here's an example:
class Shape
{
public virtual double GetArea()
{
return 0.0;
}
}
In this example, we define a base class called Shape with a virtual method called GetArea, which returns the area of the shape. We
also define a derived class called Circle that overrides the GetArea method to calculate the area of a circle.
Abstract methods - These methods do not have an implementation in the base class and must be implemented by derived classes.
They are used to define a contract that derived classes must follow. Here's an example:
In this example, we define an abstract class called Animal with an abstract method called MakeSound. We also define a derived
class called Dog that implements the MakeSound method to print "Woof!" to the console.
Extension methods - These methods allow you to add new methods to existing types without modifying the original type. Here's an
example:
In this example, we define a static class called StringExtensions with an extension method called Reverse that takes a string
and returns a new string with its characters reversed. The this keyword is used to indicate that this is an extension method for the
string type.
Method Overloading
Method overloading is a feature of object-oriented programming languages like C#, which allows defining multiple methods with the
same name in a class, but with different parameter lists. When a method is called, the compiler selects the appropriate method to
execute based on the number, types, and order of the arguments passed.
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
In this example, we define a class called Calculator with three overloaded methods called Add. The first method takes two integers
and returns their sum, the second method takes two doubles and returns their sum, and the third method takes three integers and
returns their sum.
When the Add method is called with different arguments, the C# compiler selects the appropriate method to execute based on the
number and types of the arguments passed, like this:
In this way, method overloading allows developers to create more intuitive and expressive APIs, by giving methods the same name
but different parameters, depending on the context in which they are used.
Encapsulation is the practice of hiding the internal details of a class and exposing only the essential functionality through a public
interface. In other words, encapsulation is a way to protect the internal state of an object from external modification and ensure that it
is only accessed in a controlled way.
In this example, we define a BankAccount class with private fields for the account number and balance. We also define public
methods to deposit and withdraw money from the account, as well as a method to get the current balance.
The private fields of the class are encapsulated and can only be accessed through the public interface provided by the methods. This
way, the internal state of the object is protected from accidental or intentional modification, ensuring the integrity and consistency of
the data.
Abstraction, on the other hand, is the practice of representing complex systems or concepts in a simplified way, by hiding
unnecessary details and focusing on the essential features. Abstraction allows developers to create more flexible and reusable code, by
reducing the dependencies between components and decoupling the implementation from the interface.
In this example, we define an interface called IAnimal with two methods: Eat and Sleep. We then define two classes that
implement this interface: Dog and Cat. Each class provides its own implementation of the Eat and Sleep methods, but they both
conform to the interface contract.
Using abstraction in this way, we can write code that is agnostic to the specific implementation of the animal class, allowing us to
create more modular and reusable code. We can also easily extend our code by adding new animal classes that conform to the same
interface, without having to change the existing code.
Inheritance is the practice of creating new classes by deriving from existing classes, thereby inheriting their properties and methods.
Inheritance allows developers to reuse code and build hierarchies of related classes, while also providing a way to specialize and
extend the behavior of the base class.
In this example, we define a base class called Animal with a virtual method called Speak. We then define two derived classes called
Dog and Cat, which override the Speak method with their own implementation.
Using inheritance in this way, we can create a hierarchy of related classes that share common properties and methods, while also
providing specialized behavior for each subclass.
Polymorphism, on the other hand, is the ability of objects to take on different forms or behaviors at runtime, depending on their type
or context. Polymorphism allows developers to write code that works with objects of different types, without having to know their
exact type at compile time.
In this example, we create two instances of the Animal class, but initialize them with objects of type Dog and Cat, respectively. We
then call the Speak method on each object, which outputs a different message depending on the type of the object.
Using polymorphism in this way, we can write code that works with objects of different types, as long as they conform to the same
interface or inheritance hierarchy. This allows us to create more flexible and modular code, by reducing the coupling between
components and promoting code reuse.
In this example, we define a base class called Vehicle with three properties and a virtual method called Drive. We then define a
derived class called Car, which inherits the properties and methods of the Vehicle class and adds a new method called Park.
The Drive method in the Car class overrides the behavior of the Drive method in the Vehicle class, providing a new
implementation that is specific to the Car class.
We can create instances of both the Vehicle and Car classes, and call their methods as follows:
In this example, we create an instance of the Vehicle class and an instance of the Car class. We then call the Drive method on
both objects, which outputs a different message depending on the type of the object. Finally, we call the Park method on the Car
object, which is not available on the Vehicle object, since it is specific to the Car class.
This example demonstrates the concept of inheritance, where the Car class derives from the Vehicle class, and the Drive method
is overridden in the Car class to provide a new implementation. The Park method is a new method that is specific to the Car class
and not available on the Vehicle class.
In this example, we define a base class called Animal with a virtual method called Speak. We then define a derived class called Dog,
which overrides the Speak method with a new implementation that outputs "The dog barks." instead of "The animal speaks."
When we create an instance of the Dog class and call its Speak method, the output will be "The dog barks." since the derived class
implementation is used instead of the base class implementation.
Method hiding, on the other hand, is the process of defining a new method in a derived class with the same name as a method in the
base class, but with a different implementation. The method signature of the derived class method may or may not match the method
signature of the base class method.
In this example, we define a base class called Animal with a method called Speak. We then define a derived class called Dog, which
defines a new method also called Speak with a different implementation that outputs "The dog barks." instead of "The animal
speaks."
When we create an instance of the Dog class and call its Speak method, the output will be "The dog barks." since the derived class
implementation is used instead of the base class implementation.
Note that in this example, we use the new keyword to explicitly indicate that we are hiding the base class method, and not overriding
it. If we did not use the new keyword, the C# compiler would generate a warning indicating that we are hiding the base class method
without using the new keyword, and that the base class method would still be accessible through the derived class.
A sealed class cannot be used as a base class for other classes. It means that a sealed class cannot be inherited by another class. Here's
an example of a sealed class in C#:
In this example, we define a class called MySealedClass and mark it as sealed. This class cannot be inherited by any other class in
the program.
A sealed method is a method that cannot be overridden in a derived class. Here's an example of a sealed method in C#:
In this example, we define a base class called MyBaseClass with a virtual method called MyMethod. We then define a derived class
called MyDerivedClass, which overrides the MyMethod method with a new implementation and marks it as sealed. This means that
any further derived classes, such as MyDerivedDerivedClass, cannot override this method.
Sealed classes and methods can be useful in scenarios where we want to prevent certain parts of our code from being modified or
extended by other developers. However, it's important to use these features judiciously, as they can limit the flexibility and
extensibility of our code.
An abstract class is a class that cannot be instantiated directly, but can be inherited by other classes. Abstract classes are declared
using the abstract keyword. An abstract class can contain abstract methods, as well as non-abstract methods and fields. Here's an
example of an abstract class in C#:
In this example, we define an abstract class called Shape. This class contains an abstract method called GetArea(), which does not
have an implementation. It also contains a non-abstract method called PrintArea(), which calls the GetArea() method to
calculate the area of the shape and print it to the console. Any class that inherits from the Shape class must implement the GetArea()
method.
An abstract method is a method that does not have an implementation in the base class, but must be implemented in any derived
classes. Abstract methods are declared using the abstract keyword. Here's an example of an abstract method in C#:
In this example, we define an abstract class called Animal. This class contains an abstract method called Speak(), which does not
have an implementation. Any class that inherits from the Animal class must implement the Speak() method.
Abstract classes and methods are useful for creating common interfaces and behaviors that can be shared by multiple classes. They
provide a way to define a common structure and contract for a group of related classes, without requiring a complete implementation.
To implement an interface in C#, a class must explicitly declare that it implements the interface using the : <interface> syntax in
the class definition. Here's an example of a class that implements the IShape interface:
In this example, we define a class called Rectangle that implements the IShape interface. This class provides an implementation
for the GetArea() and GetPerimeter() methods, which are specified in the IShape interface.
When a class implements an interface, it is required to provide an implementation for all of the methods, properties, events, or
indexers specified in the interface. This ensures that any code that uses the interface can be confident that the implementing class
provides the expected functionality.
Interfaces are a powerful tool for creating flexible and extensible code. They allow you to define a contract between different parts of
your code, and to swap out different implementations of that contract as needed. By using interfaces, you can write code that is more
modular, testable, and maintainable.
A static class is a class that cannot be instantiated and can only contain static members such as static methods, properties, fields, or
events. Static classes are declared using the static keyword. Here's an example of a static class in C#:
In this example, we define a static class called MathUtils. This class contains two static methods: Add() and Subtract(). These
methods can be called without instantiating the MathUtils class.
A non-static class, also known as an instance class, is a class that can be instantiated and contains instance members such as
instance methods, properties, fields, or events. Non-static classes are declared without the static keyword. Here's an example of a non-
static class in C#:
In this example, we define a non-static class called Person. This class contains two instance properties: Name and Age, and an
instance method: SayHello(). These members can only be accessed by creating an instance of the Person class.
A static method is a method that belongs to the class and not to any particular instance of the class. A static method can be called
without creating an instance of the class. Static methods are declared using the static keyword. Here's an example of a static
method in C#:
In this example, we define a static class called StringUtils with a static method called IsNullOrEmpty(). This method can be
called without creating an instance of the StringUtils class.
A non-static method, also known as an instance method, is a method that belongs to a particular instance of a class. Non-static
methods are declared without the static keyword. Here's an example of a non-static method in C#:
In this example, we define a class called Calculator with a non-static method called Add(). This method can only be accessed by
creating an instance of the Calculator class.
In summary, static classes and methods belong to the class itself and not to any particular instance of the class, while non-static classes
and methods belong to a particular instance of the class.