📕
Dan Fitz's Notes
  • README
  • Ai
    • Supervised Machine Learning
      • Introduction To Machine Learning
      • Regression With Multiple Input Variables
      • Classification
  • Csharp
    • C Sharp Advanced
      • Generics
      • Delegates
      • Lambda Expressions
      • Events
    • C Sharp Fundamentals
      • Intro To C
      • Primitive Types And Expressions
      • Non Primitive Types
      • Control Flow
      • Arrays And Lists
      • Working With Dates
      • Working With Text
      • Working With Files
      • Debugging Applications
    • C Sharp Intermediate
      • Classes
      • Association Between Classes
      • Inheritance
      • Polymorphism
      • Interfaces
  • Java
    • Inheritance Data Structures Java
      • Inheritance Polymorphism Using Overriding And Access Modifiers
      • Abstract Classes And Debugging
      • File I O And Exceptions
      • Collections Maps And Regular Expressions
    • Intro To Java
      • Introduction To Java Classes And Eclipse
      • Unit Testing Arrays And Array Lists
      • Static Variables Methods And Polymorphism Using Overloading
  • Javascript
    • Algorithms Data Structures
      • Big O Notation
      • Analyzing Performance Of Arrays And Objects
      • Problem Solving Approach
      • Problem Solving Patterns
      • Recursion
      • Searching Algorithms
      • Bubble Selection And Insertion Sort
      • Merge Sort
      • Quick Sort
      • Radix Sort
      • Data Structures Introduction
      • Singly Linked Lists
      • Doubly Linked Lists
      • Stacks And Queues
      • Binary Search Trees
      • Tree Traversal
      • Binary Heaps
    • Complete Nodejs
      • Understanding Node.js
      • REST AP Is And Mongoose
      • API Authentication And Security
      • Node.js Module System
      • File System And Command Line Args
      • Debugging Node.js
      • Asynchronous Node.js
      • Web Servers
      • Accessing API From Browser
      • Application Deployment
      • Mongo DB And Promises
    • Complete React Native
      • Working With Content
      • Building Lists
      • Navigating Users Between Screens
      • State Management
      • Handling Screen Layout
      • Setting Up An App
      • More On Navigation
      • Advanced Statement Management With Context
      • Building A Custom Express API
      • In App Authentication
    • Epic React
      • React Fundamentals
      • React Hooks
      • Advanced React Hooks
      • Advanced React Patterns
      • React Performance
    • Fireship Firestore
      • Firestore Queries And Data Modeling Course
      • Model Relational Data In Firestore No SQL
    • Functional Light Javascript
      • Intro
      • Function Purity
      • Argument Adapters
      • Point Free
      • Closure
      • Composition
      • Immutability
      • Recursion
      • List Operations
      • Transduction
      • Data Structure Operations
      • Async
    • Js Weird Parts
      • Execution Contexts And Lexical Environments
      • Types And Operators
      • Objects And Functions
      • Object Oriented Java Script And Prototypal Inheritance
      • Defining Objects
    • Mastering Chrome Dev Tools
      • Introduction
      • Editing
      • Debugging
      • Networking
      • Auditing
      • Node.js Profiling
      • Performance Monitoring
      • Image Performance
      • Memory
    • React Complete Guide
      • What Is React
      • React Basics
      • Rendering Lists And Conditionals
      • Styling React Components
      • Debugging React Apps
      • Component Deep Dive
      • Building A React App
      • Reaching Out To The Web
      • Routing
    • React Testing
      • Intro To Jest Enzyme And TDD
      • Basic Testing
      • Redux Testing
      • Redux Thunk Testing
    • Serverless Bootcamp
      • Introduction
      • Auction Service Setup
      • Auction Service CRUD Operations
      • Auction Service Processing Auctions
    • Testing Javascript
      • Fundamentals Of Testing
      • Static Analysis Testing
      • Mocking Fundamentals
      • Configuring Jest
      • Test React Components With Jest And React Testing Library
    • Typescript Developers Guide
      • Getting Started With Type Script
      • What Is A Type System
      • Type Annotations In Action
      • Annotations With Functions And Objects
      • Mastering Typed Arrays
      • Tuples In Type Script
      • The All Important Interface
      • Building Functionality With Classes
    • Web Performance With Webpack
      • Intro
      • Code Splitting
      • Module Methods Magic Comments
  • Other
    • Algo Expert
      • Defining Data Structures And Complexity Analysis
      • Memory
      • Big O Notation
      • Logarithm
      • Arrays
      • Linked Lists
      • Hash Tables
      • Stacks And Queues
      • Strings
      • Graphs
      • Trees
    • Aws Solutions Architect
      • AWS Fundamentals IAM EC 2
    • Fundamentals Math
      • Numbers And Negative Numbers
      • Factors And Multiples
      • Fractions
    • Mysql Bootcamp
      • Overview And Installation
      • Creating Databases And Tables
      • Inserting Data
      • CRUD Commands
      • The World Of String Functions
      • Refining Our Selections
      • The Magic Of Aggregate Functions
    • Random Notes
      • Understanding React Hooks
  • Python
    • Data Analysis Using Python
      • Loading Querying And Filtering Data Using The Csv Module
      • Loading Querying Joining And Filtering Data Using Pandas
      • Summarizing And Visualizing Data
    • Intro To Python
      • Course Introduction Intro To Programming And The Python Language Variables Conditionals Jupyter Notebook And IDLE
      • Intro To Lists Loops And Functions
      • More With Lists Strings Tuples Sets And Py Charm
      • Dictionaries And Files
Powered by GitBook
On this page
  • Access Modifiers
  • public
  • private
  • protected
  • internal
  • protected internal
  • Constructors and Inheritance
  • Upcasting and Downcasting
  • Upcasting
  • Downcasting
  • The as keyword
  • The is keyword
  • Boxing and Unboxing
  • Boxing
  • Unboxing
  1. Csharp
  2. C Sharp Intermediate

Inheritance

Access Modifiers

One of the core ideas that OOP tries to promote is to make your classes into black boxes. That's because if you expose the implementation details of a class, other code that uses that class will be affected if you change those implementation details.

In contrast, everything we want to make accessible is part of the public interface of a class: it's what we want to expose to our users.

To create the black box and public interface of a class, we have access modifiers. If you recall, these are the ones we have available in C#:

  • public

  • private

  • protected

  • internal

  • protected internal

public

Makes a field/property/method accessible everywhere. You use public when you want your field/property/method to be part of the public interface of the class.

public class Customer
{
  public void Promote() {}
}

var customer = new Customer();
customer.Promote();

private

Makes a field/property/method only accessible inside the class. You use private when you're dealing with the implementation details and so don't want to give open access.

public class Customer
{
  private int CalculateRating() {}

  public void Promote()
  {
    // Implementation details!
    if (CalculateRating() > 100)
    {
       // Do something...
    }
  }
}

var customer = new Customer();
customer.CalculateRating(); // fails

protected

Makes a field/property/method accessible from the class AND its derived classes.

public class Customer
{
  public int CalculateRating() {}
}

public class VIP : Customer
{
  public void Promote()
  {
    // Access to implementation details!
    if (CalculateRating() > 1000)
    {
      // Do something...
    }
  }
}

Pro tip: protected is not good practice because it's not enough of a black box. Any derived classes still are granted access! For that reason, private is preferred over protected.

internal

Makes a class itself accessible only from the same assembly. You use internal when a class you create is a useful implementation detail for all the classes in its assembly.

internal class RateCalculator {}

// In the same assembly: WORKS
var calc = new RateCalculator();

// In another assembly: FAILS
var calc = new RateCalculator();

protected internal

Makes a field/property/method accessible from the same assembly OR accessible from any derived classes.

public class Customer
{
  protected internal void Weirdo() {}
}

// In another assembly: WORKS
var customer = new Customer();
customer.Weirdo();

// In a derived class: WORKS
public class VIP : Customer {}
var vip = new VIP();
vip.Weirdo();

Pro tip: This access modifier is really weird. You likely won't use it.

Constructors and Inheritance

Suppose you create a Vehicle class and a derived Car class that inherits from Vehicle. There are 2 things to note about the Vehicle constructor:

  1. Base class constructor is always executed before the derived class constructor.

  2. Base class constructor is never inherited by the derived class.

In code, that means you have to explicitly create a new constructor for the derived class:

public class Vehicle
{
  private string _registrationNumber;

  public Vehicle(string registrationNumber)
  {
    _registrationNumber = registrationNumber;
    Console.WriteLine("Vehicle gets initialized first");
  }
}

public class Car : Vehicle
{
  public Car(string registrationNumber)
  {
    _registrationNumber = registrationNumber;
    Console.WriteLine("Car gets initialized second");
  }
}

There's 2 troubles with this though!

  1. You can't initialize _registrationNumber in the Car constructor because it's a private field. This means it's not accessible in a derived class like Car.

  2. The constructor for Car feels like repeated code. Is there a way to simplify this?

To solve this, we introduce the base keyword: it passes arguments to the base class constructor while inside the derived class.

public class Car : Vehicle
{
  public Car(string registrationNumber)
    : base(_registrationNumber)
  {
    // Initialize fields *specific* to the Car class here
    Console.WriteLine("Car gets initialized second");
  }
}

Upcasting and Downcasting

Upcasting is converting a derived class to a base class, and downcasting is converting a base class to a derived class.

Upcasting

Suppose you have a base class and derived class. To upcast, i.e., convert a derived class to its base class, C# does it using implicit type conversion.

public class Shape {}
public class Circle : Shape {}

Circle circle = new Circle();
Shape shape = circle; // upcasting

Note:

  • shape and circle are actually the same object in memory (shape == circle returns true), but they have different views.

  • circle can see all the Circle members and the Shape members.

  • In contrast, shape can only see the Shape members.

  • You'll see later how this is useful when we talk about polymorphism.

Pro tip: One common use case for upcasting is when you pass arguments to a function and/or constructor. When a parameter supports a base class, you know you can also pass the derived class, and it will be implicitly converted!

public class ImplicitCast
{
  private Shape _shape;
  public ImplicitCase(Shape shape)
  {
    _shape = shape;
  }
}

var obj = new ImplicitCast(new Circle());

Downcasting

With downcasting, you must perform explicit type conversion, which is known as casting.

public class Shape {}
public class Circle : Shape {}

Shape shape = new Shape();
Circle circle = (Circle)shape; // downcasting

Pro tip: A common use case for downcasting is when you need access to a greater view of available fields/properties/methods. For example, maybe you have a generic object, but you believe it is a Button, and you want to use the Button members.

private void ButtonClick(object sender)
{
  sender.clickDetails; // not accessible
  var button = (Button) sender;
  button.clickDetails; // accessible
}

The as keyword

When downcasting, you can't always guarantee it will work. That means there may be an InvalidCastException error, breaking the application if it's not handled.

To more elegantly solve this without creating an error, you use the as keyword:

Car car = (Car)shape; // throws error

Car car = shape as Car; // returns null if fails
if (car != null)
{
  // Do something...
}

The is keyword

Alternatively, instead of as where you cast first and then find out if it failed, you can use is to check if it's possible to cast.

if (shape is Car)
{
  Car car = (Car)shape;
}

Boxing and Unboxing

If you recall, there are value types and reference types.

Value types:

  • Stored in stack where items in memory get removed immediately after they go out of scope.

  • Stack also has more limited amount of memory.

  • Examples: int, bool, char

Reference types:

  • Stored in heap where items require a longer lifetime.

  • Heap also has a lot more memory allocation.

  • Examples: any classes like objects, arrays, strings

Additionally, we also know that object is the base class of all classes.

object obj = new Shape();

Boxing

What happens if we implicitly convert a value type into an object? This is known as boxing:

object obj = 10;

Behind the scenes, the CLR boxes the value 10 and stores it in the heap. Then it places a reference to that object in the stack.

Note: Creating an object has a performance cost, so be aware of this.

Unboxing

Unboxing is exactly what you think: after a value type has been boxed in a reference in the heap, you can extract the value and put it back in the stack by casting the reference.

object obj = 10;
int number = (int)obj;
PreviousAssociation Between ClassesNextPolymorphism

Last updated 3 years ago