You are on page 1of 24

Object Oriented Programming using C# SKNSCOEK

Unit 3: Object Oriented Programming using C#

Objects and Reference Types

The Types in .NET Framework are either treated by Value Type or by Reference Type. A Value
Type holds the data within its own memory allocation and a Reference Type contains a pointer
to another memory location that holds the real data. Reference Type variables are stored in the
heap while Value Type variables are stored in the stack.

Value Type:

A Value Type stores its contents in memory allocated on the stack. When you created a Value
Type, a single space in memory is allocated to store the value and that variable directly holds a
value. If you assign it to another variable, the value is copied directly and both variables work
independently. Predefined datatypes, structures, enums are also value types, and work in the
same way. Value types can be created at compile time and Stored in stack memory, because of
this, Garbage collector can't access the stack.

e.g.

int x = 10;

Here the value 10 is stored in an area of memory called the stack.

Reference Type:

Reference Types are used by a reference which holds a reference (address) to the object but not
the object itself. Because reference types represent the address of the variable rather than the
data itself, assigning a reference variable to another doesn't copy the data. Instead it creates a
second copy of the reference, which refers to the same location of the heap as the original

1
Object Oriented Programming using C# SKNSCOEK

value. Reference Type variables are stored in a different area of memory called the heap. This
means that when a reference type variable is no longer used, it can be marked for garbage
collection. Examples of reference types are Classes, Objects, Arrays, Indexers, Interfaces etc.

e.g.

int[] iArray = new int[20];

In the above code the space required for the 20 integers that make up the array is allocated on
the heap.

Stack and Heap

Stack is used for static memory allocation and Heap for dynamic memory allocation, both
stored in the computer's RAM.

Inheritance
Inheritance allows a class to absorb or inherit all the properties and methods of another class. It
is like a parent-child relationship where the child inherits some properties from his/her parents
such as facial features and attitudes. The base class or the parent class, is the class which is
inherited by other classes. The derived class or the child class, is the one inheriting from the base
class. All the methods and properties of the base class can be used by the derived class except for
the private members and methods. We will soon discover that all the classes in the .NET
Framework inherits the Object class. We will demonstrate the basic concept of inheritance in the
following example program. Create a seperate class file and write the following code.

using System;

namespace InheritanceDemo
{
class Parent
{
private string message;

public string Message

2
Object Oriented Programming using C# SKNSCOEK

{
get { return message; }
set { message = value; }
}

public void ShowMessage()


{
Console.WriteLine(message);
}

public Parent(string message)


{
this.message = message;
}
}

class Child : Parent


{
public Child(string message)
: base(message)
{

}
}
}

Example 1 - Inheritance Demo

We defined two classes named Parent and Child. We include a private data member (line 7) and
its corresponding public property(lines 9-13). We also defined a method that will show the
message. A constructor was defined for the Parent class that accepts a string argument that will
serve as the message. Now take a look at the Child class (line 26). It inherits the Parent class and
absorbs all its methods and properties. The syntax for inheriting a class is as follows.

class DerivedClass : BaseClass

You simply place a colon (:) after the name of the class and then indicate the class that it will
inherit. Inside the Child class is a single constructor that also accepts a string argument. When
creating inherited classes, both the derived class constructor and the base class default
constructor are executed. A default constructor is a no-parameter constructor. If no constructor
was defined in a class, the compiler automatically creates a default constructor.

If we want to call the base class constructor when we call the derived class contructor, we need
to use the base keyword. The base keyword calls a constructor of the base class. We need to
write a colon before using the base keyword. By supplying the value of the message parameter of
the derived class constructor and passing it inside the parentheses of the base keyword, it will
call the matching contructor of the base class and pass the value of the message. The Parent's
class constructor will then assign the value of the message into its private data member. You can
add some code inside the body of the Child contructor and it will be executed after the Parent's
constructor. If we omit the base keyword, the default constructor of the base class will be called
instead.

3
Object Oriented Programming using C# SKNSCOEK

Let's now create the actual Parent and Child objects to demonstrate that Child really inherits the
methods and properties of the Parent class.

namespace InheritanceDemo
{
class Program
{
static void Main()
{
Parent myParent = new Parent("Message from parent.");
Child myChild = new Child("Message from child.");

myParent.ShowMessage();

myChild.ShowMessage();

myParent.Message = "Modified message of the parent.";


myParent.ShowMessage();

myChild.Message = "Modified message of the child.";


myChild.ShowMessage();

//myChild.message; ERROR: can't access private members of base


class
}
}
}

Interfaces
Interfaces are similar to classes, but it only contains declarations for methods and properties.
Interfaces can be considered as plugins to classes. The class that implements a particular
interface is required to make an implementation for the members and methods of that interface.
Let's take a look at how we can define and use an interface on a class.

using System;

namespace InterfaceDemo
{
interface ISample
{
void ShowMessage(string message);
}

public class Sample : ISample


{
public void ShowMessage(string message)
{
Console.WriteLine(message);
}
}

class Program

4
Object Oriented Programming using C# SKNSCOEK

{
public static void Main()
{
Sample sample = new Sample();

sample.ShowMessage("Implemented the ISample Interface!");


}
}
}

Example 1 - Using an Interface

Implemented the ISample Interface!

We defined an interface named ISample. As a naming convention, interfaces use Pascal Casing
and all interfaces should start with letter I. We defined a method inside the interface (line 7).
You may notice that the method has no body. When defining methods inside an interface, you
only need to write the header of the method. Also, note that methods and properties defined
inside an interface cannot have access specifiers since they should always be accessed by
implementing classes.

When a class needs to implement an interface, we use the same syntax as the one we used for
inheritance. The class that implemented the interface provide the actual code for the members of
that interface. As you can see in our Sample class, it provided an implementation for the
ShowMessage() method of the ISample interface.

You are not limited to implementing just one interface for a class.

class Sample : ISample1, ISample2, ISample3


{
//Implement all interfaces
}

You can supply as many interfaces as you want but make sure that the class that implement
multiple interfaces will provide implementations for all the members of those interfaces. If a
class is inheriting a base class and implementing interfaces at the same time, the base class
should go first in the list before the interfaces.

class Sample : BaseClass, ISample1, ISample2


{
}

You can also use the is operator to check if a particular object implements an indicated interface.

Sample sample = new Sample();

if(sample is ISample)
{
Console.WriteLine("sample implements the ISample Interface!");
}

5
Object Oriented Programming using C# SKNSCOEK

Another thing to note is you cannot create instances of an interface because it has no constructor.
The following code is illegal.

ISample sample = new ISample();

The following is an example of an interface that has a property.

interface ISample
{
int Number { get; set; }
}

You do not provide any code for the getter and the setter. The class that will implement the
interface will handle that.

class Sample : ISample


{
private int number;

public int Number


{
get { return number; }
set { number = value; }
}
}

Interfaces can even implement other interfaces. Consider the following example.

using System;

namespace InterfaceDemo2
{
interface IBase
{
void BaseMethod();
}

interface ISample : IBase


{
void ShowMessage(string message);
}

public class Sample : ISample


{
public void ShowMessage(string message)
{
Console.WriteLine(message);
}

public void BaseMethod()


{
Console.WriteLine("Method from base interface!");
}
}

6
Object Oriented Programming using C# SKNSCOEK

class Program
{
public static void Main()
{
Sample sample = new Sample();

sample.ShowMessage("Implemented the ISample Interface!");


sample.BaseMethod();
}
}
}

Example 2 - Interface Which Implement Another Interface

You can see that even if the Sample class only implemented the ISample interface, it needs to
implement all the members of IBase since ISample inherited from it.

Abstract Classes

Polymorphism
Polymorphism allows classes to transform into a different class within its inheritance hierarchy.
It allows a programmer to use objects of different types but treat them in their "general" form
while maintaining their specific functionality. For example, there are many kinds of animals such
as dogs, cats, and birds, but you can treat them generally as animals. All animals eat food, but
they eat in different ways. All animals can move, but they move in different ways. We don't care
about how they do them specifically. It is up to each object to use its own implementation of
those functions. This is the essence of polymorphism. In a program, for example, you can create
several instances of the Animal class. You can assign instances of other classes to it as long as its
type is a subtype of Animal thanks to inheritance. Let's look at an example behavior of
polymorphism.

using System;

namespace PolymorphismDemo
{
class Animal
{
public virtual void Eat()
{

7
Object Oriented Programming using C# SKNSCOEK

Console.WriteLine("The animal ate!");


}
}

class Dog : Animal


{
public override void Eat()
{
Console.WriteLine("The dog ate!");
}
}

class Bird : Animal


{
public override void Eat()
{
Console.WriteLine("The bird ate!");
}
}

class Fish : Animal


{
public override void Eat()
{
Console.WriteLine("The fish ate!");
}
}

class Program
{
public static void Main()
{
Dog myDog = new Dog();
Bird myBird = new Bird();
Fish myFish = new Fish();
Animal myAnimal = new Animal();

myAnimal.Eat();

myAnimal = myDog;
myAnimal.Eat();
myAnimal = myBird;
myAnimal.Eat();
myAnimal = myFish;
myAnimal.Eat();
}
}
}

Example 1 - Polymorphism Demo

The animal ate!


The dog ate!
The bird ate!
The fish ate!

8
Object Oriented Programming using C# SKNSCOEK

We defined 4 different classes. The Animal is the base class and will be derived by the other
three classes. Each of the class has their own version of the Eat() method. We created instances
for each class. We then called the Eat() method of the Animal instance as shown below.

Animal myAnimal = new Animal();

myAnimal.Eat();

The polymorphism takes place next. We assigned the Dog object to the Animal instance and
called the Eat() method again. Now even though we are using an Animal instance, the Eat()
method of the Dog class was called. This is the effect of polymorphism. We then assigned the
other two objects (Bird and Fish) to the Animal instance and called their respective Eat methods.
Notice that when we assigned the objects to the Animal instance, we didn't use casting. This is
because no casting is required if you are storing an object of a derived class to an object that has
a type which is higher than that of the derived class in the inheritance hierarchy.

You can also initialize an Animal class with the constructor of any of its derived classes.

Animal myDog = new Dog();


Animal myBird = new Bird();
Animal myFish = new Fish();

myDog.Eat();
myBird.Eat();
myFish.Eat();

The code above will yield the same output as Example 1. Let's modify our program to show
another concept of polymorphism.

using System;

namespace PolymorphismDemo2
{
class Animal
{
public virtual void Eat()
{
Console.WriteLine("The animal ate!");
}
}

class Dog : Animal


{
public override void Eat()
{
Console.WriteLine("The dog ate!");
}

public override void Run()


{
Console.WriteLine("The dog ran!");
}

9
Object Oriented Programming using C# SKNSCOEK

class Bird : Animal


{
public override void Eat()
{
Console.WriteLine("The bird ate!");
}

public override void Fly()


{
Console.WriteLine("The bird flew!");
}
}

class Fish : Animal


{
public override void Eat()
{
Console.WriteLine("The fish ate!");
}

public override void Swim()


{
Console.WriteLine("The fish swam!");
}
}

class Program
{
public static void Main()
{
Animal animal1 = new Dog();
Animal animal2 = new Bird();
Animal animal3 = new Fish();

Dog myDog = (Dog)animal1;


Bird myBird = (Bird)animal2;
Fish myFish = (Fish)animal3;

myDog.Run();
myBird.Fly();
myFish.Swim();
}
}
}

Virtual and Override keyword


Virtual methods are methods of a base class that can be overridden by a method in a derived
class to provide a different implementation of that method. For example, you have method A
from Class A and Class B inherits from Class A, then method A will be available from Class B.
But method A is the exact method you inherited from Class A. What if you want to have a

10
Object Oriented Programming using C# SKNSCOEK

different behavior for the inherited method? Virtual methods solve this problem. Consider the
following example.

using System;

namespace VirtualMethodsDemo
{
class Parent
{
public virtual void ShowMessage()
{
Console.WriteLine("Message from Parent.");
}
}

class Child : Parent


{
public override void ShowMessage()
{
Console.WriteLine("Message from Child.");
}
}

class Program
{
public static void Main()
{
Parent myParent = new Parent();
Child myChild = new Child();

myParent.ShowMessage();
myChild.ShowMessage();
}
}
}

Example 1 - Virtual Methods Example

Message from Parent.


Message from Child.

A virtual method can be defined by placing the virtual keyword in the declaration of the method.
The virtual keyword indicates that the method can be overridden or, in other words, can have a
different implementation. The class that inherits the Parent class contains the method that
overrides the virtual method. You use the override keyword to indicate that a method should
override a virtual method from the base class.

You can use the base keyword to call the original virtual method inside the overriding method.

using System;

namespace VirtualMethodsDemo2
{

11
Object Oriented Programming using C# SKNSCOEK

class Parent
{
private string name = "Parent";

public virtual void ShowMessage()


{
Console.WriteLine("Message from Parent.");
}
}

class Child : Parent


{
public override void ShowMessage()
{
base.ShowMessage();
Console.WriteLine("Message from Child.");
}
}

class Program
{
public static void Main()
{
Parent myParent = new Parent();
Child myChild = new Child();

myParent.ShowMessage();
myChild.ShowMessage();
}
}
}

Base keyword

If you only use the base method and no other codes inside the overriding method, then it has a
similar effect to not defining the overriding method at all. You cannot override a non-virtual
method or a static method. The overriding method must also have similar access specifier as the
virtual method. You can create another class that inherits the Child class and override its
ShowMessage again and define a different implementation. If you want the overriding method to
be final, that is, it cannot be overridden by the other classes that will derive from the class it
belongs, you can use the sealed keyword.

Example 2 - Using the base Keyword

Message from Parent.


Message from Parent.
Message from Child.

12
Object Oriented Programming using C# SKNSCOEK

public sealed override void ShowMessage()

Now if another class inherits from the Child class, it cannot override the ShowMessage()
anymore.

Let's have another example, we will override the ToString() method of the System.Object. Every
class in C# including the ones you created inherits System.Object as you will see in the next
lesson.

using System;

namespace VirtualMethodsDemo3
{
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }

public override string ToString()


{
return FirstName + " " + LastName;
}
}

class Program
{
public static void Main()
{
Person person1 = new Person();

person1.FirstName = "John";
person1.LastName = "Smith";

Console.WriteLine(person1.ToString());
}
}
}

System.Object Class

All classes in the .NET Framework inherits from the System.Object class. The System.Object
class is mapped to the object keyword in C#. For simplicity, I will just use object to refer to
System.Object. The following is a list of some common methods of the object class.

13
Object Oriented Programming using C# SKNSCOEK

Method Return Type Virtual Static Description


This is the constructor of
Object() None No No
the System.Object class.
This is the destructor of
~Object() None No No
the System.Object class.
Accepts another object as the
argument and returns true if
Equals(object) bool Yess No
the object refers to the same
object that called this method.
A static method that accepts
two objects and determines
Equals(object, object) bool No Yes
if they are referring to the same
object.
A static method that determines
ReferenceEquals
bool No Yes if the two objects are the
(object,object)
same instance.
Returns a string representation
of the object. You can override
ToString() string Yes No this method to provide your
own string representation
for your class.
This method creates a new instance
MemberwiseClone() object Noo No of the object and copies the
values of its data members.
Returns the type of the
GetType() System.Type No No
object as System.Type
Returns a hash value which
GetHashCode() int Yes No
identifies the object state.

Figure 1 - System.Object Members

Not all of this methods are used frequently. Since all classes in C# inherits from this class, they
also possess these methods except for the static methods. When you are creating a class, the class
implicitly inherits from the object class. So when you are declaring a class, this is the actual code
read by the compiler.

class MyClass : System.Object


{
}

The purpose of why every class in .NET inherits from object is to take advantage of
polymorphism which will be discussed later. For example, one of the overloads of the
Console.WriteLine() method accepts an object argument. That's why you can pass almost
anything as an argument to the Console.WriteLine() method. To demonstrate that anything is an
object in C#, let's take a look at a sample program that follows.
14
Object Oriented Programming using C# SKNSCOEK

using System;

namespace ObjectDemo
{
public class Program
{
public static void Main()
{
int myInt = 1;
double myDouble = 4.0;
string myString = "Hello";

Console.WriteLine(myInt.ToString());
Console.WriteLine(myDouble.ToString());
Console.WriteLine(myString.ToString());
}
}
}

Example 1 - Everything Inherits from System.Object Class

You can see that int, double, and string objects all called the ToString() method which is a
method inherited from the object class.

Sealed Keyword
Sealed classes are classes that cannot be inherited by other class. Since sealed classes cannot be
inherited, they cannot be abstract. The following program shows an example of a sealed class.

namespace SealedClassesDemo
{
public sealed class Base
{
private int someField;

public int SomeProperty


{
get { return someField; }
set { field = value; }
}

public void SomeMethod


{
//Do something here
}

//Constructor
public Base()
{
//Do something here
}
}

15
Object Oriented Programming using C# SKNSCOEK

public class Derived: Base // ERROR


{
//This class cannot inherit the Base class
}
}

We use the sealed keyword to indicate that a class is a sealed class. You can see that sealed
classes are just like normal classes so it can also have fields, properties and methods. Line 25
will produce an error because the Derive class is deriving from the sealed Base class. Sealed
classes are useful if you are making a class that prohibits itself from being inherited by others.

New keyword in context of method overriding


New keyword is also used in polymorphism concept, but in the case of method overloading So
what does overloading means, in simple words we can say procedure of hiding your base class
through your derived class.

It is implemented as:

class A
{
public void show()
{
Console.WriteLine("Hello: Base Class!");
Console.ReadLine();
}
}

class B : A
{
public new void show()
{
Console.WriteLine("Hello: Derived Class!");
Console.ReadLine();
}
}

Here’s a simple implementation in C# without using any keyword. Do you think it will run fine
or show some RUN or COMPILE time errors?

Let’s see:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

16
Object Oriented Programming using C# SKNSCOEK

namespace Generics
{
class A
{
public void show()
{
Console.WriteLine("Hello: Base Class!");
Console.ReadLine();
}
}

class B : A
{
public void show()
{
Console.WriteLine("Hello: Derived Class!");
Console.ReadLine();
}
}

class Polymorphism
{
public static void Main()
{
A a1 = new A();
a1.show();
B b1 = new B();
b1.show();
A a2 = new B();
a2.show();

}
}
}

Boxing and Unboxing


Boxing is the process of converting a value type such as a structure into an object or a reference
type. Unboxing is the opposite the opposite process and converts reference types to value types.
Example 1 demonstrates boxing.

namespace BoxingAndUnboxingDemo
{
struct MyStruct
{
public int Number { get; set; }
}

class Program
{
public static void Main()

17
Object Oriented Programming using C# SKNSCOEK

{
MyStruct valueType = new MyStruct();
valueType.Number = 10;
object refType = valueType;
}
}
}

Example 1 - Boxing Demo

We created a structure named MyStruct and provide a property for testing purposes. To box a
value type, simple assign it into an object variable. The refType contains an address to a
MyStruct type and not the address of the original variable valueType. This can be shown by
unboxing the refType back into a value type MyStruct.

MyStruct valueType2 = (MyStruct)refType;

We use casting to convert the reference type refType variable back into a MyStruct value type
variable.

Type Casting: Up casting and Down casting


Up-casting

Now we come on to one of the original goals of the article. With the simple example above of
polymorphism, you should be able to quickly understand what up-casting is, in fact we have
already used up-casting in our example.

The diagram below is a UML diagram for our polymorphism example.

Figure 1: UML Diagram For Shape.

Consider the following code:

18
Object Oriented Programming using C# SKNSCOEK

Shape s = new Circle(100, 100);

We have cast Circle to the type Shape. This is perfectly legal code (as we saw in the
Polymorphism example). This is possible, because Circle has been derived from Shape and you
expect all methods and properties of Shape to exist in Circle. Executing the Draw method by
doing s.Draw() gives the following output:

Drawing a CIRCLE at 100,100

If we had declared the Draw method in Circle as follows, public new void Draw() the output
would have been:

Drawing a SHAPE at 100,100

As we have already mentioned, marking the method with new, tells the compiler that we are
not overriding the base class implementation of the method.

So why is this called up-casting? Consider the diagram above. From Circle, we are moving up
the object hierarchy to the type Shape, so we are casting our object "upwards" to its parent
type.

Up-casting is implicit and is safe. What do we mean by safe? Well, we can happily
cast Circleto Shape and expect all the properties and methods of Shape to be available.

Down-casting

The flip side of the coin to up-casting is ...yes you guessed it, down-casting. Down-casting
takes a little more understanding and you have to be very careful when down-casting types.

To help us better understand down-casting, we are going to add a new method to


our Circleclass. This will be a simple method called FillCircle.

public void FillCircle()


{
Console.WriteLine("Filling CIRCLE at {0},{1}", m_xpos, m_ypos);
}

Using the example from up-casting, we know that we are able to write the following:

Shape s = new Circle(100, 100);

We are then free to call the Draw method. Having added the FillCircle method to our Circle
class, are we able to call this method by doing the following?

s.FillCircle ();

In short, the answer is no. Because we have cast Circle to the type Shape, we are only able to
use methods found in Shape, that is, Circle has inherited all the properties and methods of
Shape. If we want to call FillCircle, we need to down-cast our type to Circle. Why is it called
down-casting? Quite simply, we are moving down the object hierarchy, from Shape down to
Circle.

19
Object Oriented Programming using C# SKNSCOEK

So how do we code a down-cast from Shape to Circle? The code for doing this is quite simple:

Circle c;
c = (Circle)s;

Simply, we are declaring c as the type Circle and explicitly casting s to this type. We are now
able to call the FillCircle method by doing the following:

c.FillCircle();

This gives us the following output:

Drawing a CIRCLE at 100,100


Filling CIRCLE at 100,100

We could also write ((Circle)s).FillCircle() reducing the lines of code needed to down-cast our
type and call the required method.

Down-casting is potentially unsafe, because you could attempt to use a method that the
derived class does not actually implement. With this in mind, down-casting is always explicit,
that is, we are always specifying the type we are down-casting to.

The is Operator
The is operator in C# allows you to test if an object can be converted into another object mainly
by using casting. The is operator requires two operands, the one that will be tested, and the type
to which that object will be compared. The is operator returns a boolean value. For example,
supposed we have a class named Animal, then we created an instance of it:

using System;

namespace IsOperatorDemo
{
class Animal
{

class Program
{
public static void Main()
{
Animal myAnimal = new Animal();

if (myAnimal is Animal)
{
Console.WriteLine("myAnimal is an Animal!");
}
}
}
}

20
Object Oriented Programming using C# SKNSCOEK

Example 1 - Using the is Operator

myAnimal is an Animal

You see the is operator in action. We use it as the condition for our if statement. It checked if the
myAnimal object is an instance of an Animal. Since that was true, the code inside the if
statement executed.

The is operator can also check if a particular object is in the inheritance hierarchy of a particular
type. Consider the next example:

using System;

namespace IsOperatorDemo2
{
class Animal
{

class Dog : Animal


{

class Program
{
public static void Main()
{
Dog myDog = new Dog();

if (myDog is Animal)
{
Console.WriteLine("myDog is an Animal!");
}
}
}
}

Example 2 - Is Operator Also Checks for Inheritance

myDog is an Animal!

So we created another class named Dog which is a derived class of Animal. We created a new
Dog instance and then test if it is an Animal or a derived class of Animal using the is operator.
Since the Dog inherits Animal, expression results to true. If you will transform this in a sentence:
"My Dog is an Animal". What will happen if we reverse it?

Animal myAnimal = new Animal();

if (myAnimal is Dog /* Error */)


{

21
Object Oriented Programming using C# SKNSCOEK

Console.WriteLine("myAnimal is a Dog!");
}

This will not produce an error. The expression will only result to false. This makes sense since
not all animals are dogs, but all dogs are animals.

An alternative way to check for the type of an object is by using the typeof operator and the
GetType() method of the System.Object class.

if (myAnimal.GetType() == typeof(Animal))
{
}

The GetType() method returns a System.Type object which indicates the type of the object that
called it. The typeof operator accepts the name of a type and returns its corresponding
System.Type object as well.

The as Operator

You can use the as operator in C# to convert a class into another class within the same
inheritance hierarchy. The as operator is equivalent to using casting with some minor differences
as will be explained later. The following shows the syntax of using the as operator.

myObject as DestinationType;

The left operand is the object that will be converted and the right operand is the destination type
for which that object will be converted to. The following codes are equivalent.

Destination someObject = (Destination)myObject;


Destination someObject = myObject as Destination;

The first code where you used casting will cause an exception if the conversion fails—that is if
the conversion is not possible for the two classes. The second code where you used the as
operator will return null if the conversion has failed. You could also use the as operator when
calling a method of a derived class through its base class. Refer to the classes from the last
lesson.

(myAnimal as Dog).Run();

The above code will throw a NullReferenceException if the myAnimal object cannot be
converted to a Dog instance.

22
Object Oriented Programming using C# SKNSCOEK

23
Object Oriented Programming using C# SKNSCOEK

24

You might also like