You are on page 1of 10

University of Hertfordshire

School of Computer Science


BSc Computer Science(SWE)

Module: Programming Paradigms


C# Essay

Alex Williams
Level 6(2022-23)
Table of Contents

1.0 Introduction 1
2.0 Language Features 1
3.0 Comparisons 5
3.1 Java 5
3.2 Clojure 6
3.3 BASIC 6
3.4 Scala 6
3.5 Ruby 7
4.0 Conclusions & Summary 7
5.0 References 8

1.0 Introduction
C# is a statically-typed, object-oriented programming language.

This essay will discuss the C# language, including its semantic and syntactic aspects. It will
also describe its relation and differences with Java, Scala, Clojure, Ruby, and BASIC. The
focus of the content will be primarily on inheritance and how it is achieved in these
languages.

2.0 Language Features


Inheritance is a fundamental concept in object-oriented programming (OOP) that allows
classes to inherit properties and behaviours from other classes. In C#, inheritance is
implemented using the class keyword, allowing one class to derive from another.

To declare a class that inherits from another class in C#, the derived class uses a colon
followed by the name of the base class. The general syntax for declaring a derived class in
C# is as follows:

class ChildClass : ParentClass


{
// ChildClass code
}

Fig. 1 – Code that shows the syntax of a class inheriting from another.

To illustrate this further, the following examples relate to implementing a Shape class, from
which several unique shape types will inherit.

First, let’s consider a base class called Shape. The following code shows how Shape has
been implemented to be inheritable.

1
public abstract class Shape
{
public abstract double GetArea();
}

Fig. 2 – Code showing the implementation of the Shape example.

In this example, the Shape class has been made inheritable using the abstract modifier.
The abstract modifier in the class header declares that this class is intended to be a base
class in which it is only instantiated by other classes, not on its own (Microsoft, 2021 A).
There’s also the abstract method GetArea(), which non-abstract classes inherited from
Shape will implement in their own way (Microsoft, 2021 B).

After implementing the inheritable Shape class, several other classes can derive from it. The
following code shows the implementation of a Circle, Rectangle, and Ring class.

public class Circle : Shape


{
private double radius;

public Circle(double radius)


{
this.radius = radius;
}

public override double GetArea()


{
return Math.PI * Math.Pow(radius, 2);
}
}

Fig. 3 – Code implementation of the Circle example.

public class Rectangle : Shape


{
private double length;
private double width;

public Rectangle(double length, double width)


{
this.length = length;
this.width = width;
}

public override double GetArea()


{
return length * width;
}
}

Fig. 4 – Code implementation of the Rectangle example.

public class Ring : Shape

2
{
private double innerRadius;
private double outerRadius;

public Ring(double innerRadius, double outerRadius)


{
this.innerRadius = innerRadius;
this.outerRadius = outerRadius;
}

public override double GetArea()


{
return Math.PI * (Math.Pow(outerRadius, 2) - Math.Pow(innerRadius,
2));
}
}

Fig. 6 – Code implementation of the Ring example.

In these examples, the Circle, Rectangle, and Ring classes inherit from the
aforementioned Shape class. Each contains unique properties that define them (such as
the Circle class’ radius). Using overriding, they each contain a unique implementation of
the GetArea() method, which they all inherit from the Shape class.

Inheritance allows inherited classes to access private methods declared in the base class.
The following is an example of implementing that in the Shape class.

public abstract class Shape


{
private string colour;

public string GetColour()


{
return colour;
}

public void SetColour(string colour)


{
this.colour = colour;
}

public abstract double GetArea();


}

Fig. 7 – Implementation of the GetColour() and SetColour() examples.

In this implementation, the colour field is private, meaning it can only be accessed within
the Shape class. The GetColour() method returns the value of colour, and the
SetColour() method sets the value of colour.

3
The GetColour() and SetColour() methods have not been declared abstract, meaning
they are already part of the Shape class and can be used directly by inherited classes.

For example, if there was a Circle object named circle, its colour can be set by calling
circle.SetColour(“green”). Furthermore, the value of colour can be retrieved by
calling circle.GetColour().

Finally, to show the Shape class and its derived classes in action, a main() class can be set
up to facilitate this. The following is an example of that implementation.

public class Program


{
static void Main(string[] args)
{
Circle circle = new Circle(3);
circle.SetColor("blue");

Rectangle rectangle = new Rectangle(4, 5);


rectangle.SetColor("green");

Ring ring = new Ring(2, 4);


ring.SetColor("purple");

Shape[] shapes = new Shape[] { circle, rectangle, ring };

for (int i = 0; i < shapes.Length; i++)


{
double area = shapes[i].GetArea();
string color = shapes[i].GetColor();
Type type = shapes[i].GetType();

Console.WriteLine("Area of " + color + " " + type + ": " + area);


}
}
}

Fig. 8 – A Program class testing the functionality of the previous examples in Main().

In this example, each of the derived classes are instantiated as objects. Each shape also
has a colour set to it using SetColour(). The objects are then added into an array called
shapes. Arrays in C# are instantiated by declaring their type and the array's name
(Microsoft, 2021). Finally, a for loop is used to iterate the properties of each shape, including
its area.

The purpose of this example was to illustrate how to show that the aforementioned code
works by storing the objects in an array and outputting each shape object’s area. The
results from the terminal were as follows:

Area of blue Circle: 28.274333882308138


Area of green Rectangle: 20

4
Area of purple Ring: 37.69911184307752

Process finished with exit code 0.

Fig. 9 – The resulting areas of the previous example at runtime.

3.0 Comparisons
In this section, comparisons of the Shape example between C# and the aforementioned
languages will be made. They will highlight these languages' syntactic, semantic, and
pragmatic differences against C#.

3.1 Java
In Java, the extends keyword indicates that a class inherits from another. However, in C#,
inheritance is defined using a colon (:). Here is an example of defining inheritance in Java:

class ChildClass extends ParentClass


{
// ChildClass code
}

Fig. 10 – Example of declaring inheritance in Java.

In the context of the Shape examples, Java achieves inheritance by defining a base class
called `Shape` and various derived classes such as Circle. Similarly to the example above,
the method’s signature reads as `public class Circle extends Shape`.

Semantically, some differences exist in how inheritance is implemented in Java and C#. In
Java, all classes inherit from the Object class, and all methods are virtual by default,
meaning a subclass can override them (Oracle, 2022). In C#, however, methods are
non-virtual by default and must be explicitly marked with the virtual or abstract keywords to
allow for overriding.

Pragmatically, these differences in semantics can lead to different coding practices. In Java,
marking all methods with the @Override annotation is common to ensure that the compiler
knows a method overrides a superclass method (Oracle, 2022). While C# doesn’t have the
same concept of annotations, the override keyword is used in a method’s signature to state
it explicitly overrides an inherited method (Microsoft, 2022).

Neither C# nor Java supports traditional multiple inheritances, where a class can inherit
from multiple classes directly. However, they both support multiple inheritances of
interfaces, allowing a class to implement multiple interfaces.

3.2 Clojure
Clojure does not support inheritance in the same way as object-oriented languages like C#.
This is because Clojure is a functional programming language that emphasises composition
over inheritance.

5
Clojure does not have classes in the traditional object-oriented sense. Instead, it uses data
structures and functions to define the structure and behaviour of objects. Inheritance is
achieved through protocols, which define a set of functions a type must implement to
satisfy the protocol (Hickey, 2022).
Using the Shape examples, the following figure will show how inheritance is achieved in
Clojure.

From a pragmatic standpoint, Clojure's approach to inheritance can be seen as more


functional and suitable for complex systems requiring high flexibility and adaptability
(Agilliway, 2022). C#'s approach, on the other hand, may be better suited for more
traditional object-oriented systems.

3.3 BASIC
In its earliest versions, like that shown in the module examples, BASIC doesn’t have built-in
support for inheritance like C# does and, therefore, cannot be compared solely on its
implementation of inheritance.

More general syntactic differences between the two languages could be discussed. For
example, BASIC defines its keywords in upper-case, whereas C# uses lower-case.

Pragmatically, C# is a modern, object-oriented programming language designed for building


complex software systems, while BASIC was developed as a simple language for teaching
beginners the basics of programming. As such, C# offers more advanced features and a
larger set of libraries and tools compared to BASIC.

3.4 Scala
Similar to Java, Scala uses the extends keyword to inherit from a class, whereas C# uses
the ‘:’ colon operator. In both languages, the override keyword is required to indicate
that a method overrides a parent method (Scala, n.d.).

The Scala example uses the trait keyword to define a mixin-style composition, which
allows for multiple inheritances of traits and avoids the diamond problem (Nikolov, 2018),
while C# only supports single inheritance. Both languages also use the abstract keyword to
define base classes.

Using the Shape examples, traits can be illustrated like this:

trait Colour {
var colour = "Red"

def getColour():String = colour


def setColour(newColour:String) = {
colour = newColour

6
}
}

Fig. 11 – Trait example implementation in Scala.

This shows how a trait `Colour` contains methods of getting and setting the colour through
its relevant methods. It can be used in other classes, such as how the examples use the
`extends` keyword to implement inheritance between the Colour trait and the abstract
Shape class, as shown in the following figure.

abstract class Shape extends Colour {


def getArea():Double
}

Fig. 12 – Implementation of the Colour trait in the abstract Shape class.

3.5 Ruby
In the Ruby example, the syntax for inheritance is denoted by the "<" operator, whereas in
C#, it is denoted by the ":" operator. In C#, a derived class can call the constructor of its
base class using the base keyword, whereas Ruby uses the super keyword. Ruby also
has no concept of an abstract class, unlike C#, so the implementation can differ
syntactically.

In C#, a class can inherit from only one base class, whereas in Ruby, a class can inherit
from multiple base classes. Ruby implements multiple inheritances using mixins (Cult3,
2015) like Scala can.

In Ruby, inheritance is less commonly used than composition, where objects are composed
of other objects to achieve the desired behaviour (Kolleger, 2017). However, Ruby can still
use inheritance to share behaviour among related classes.

C# is also statically typed, while Ruby is dynamically typed. In C#, the type of an object is
known at compile time, whereas in Ruby, the type of an object is determined at runtime
(Draper, 2019). This can affect how inheritance is used in the two languages.

4.0 Conclusions & Summary


In summary, inheritance is a fundamental feature of object-oriented programming
languages that allows the creation of new classes by reusing existing classes. It provides
code reusability and promotes the extensibility and maintenance of code.

In terms of syntax, each language has its own unique way of defining classes, inheritance,
and related constructs. C# and Java are similar in syntax as both are derived from the C
language family, while Clojure, Ruby, and Scala are different in syntax as they are
dynamically typed and functional programming languages.

7
Regarding semantics, each language has its own way of implementing inheritance. C# and
Java use the concept of class-based inheritance, where a class can inherit properties and
methods from a base class. On the other hand, Clojure uses a combination of data
structures and functions to achieve a similar effect, while Ruby and Scala use mixins to
achieve multiple inheritances.

Pragmatically, each language has its own advantages and disadvantages when using
inheritance. C# and Java are widely used in enterprise software development and have rich
tools and libraries for object-oriented programming (Ravindra, 2022). Clojure, Ruby, and
Scala are popular among developers who prefer functional programming and value
simplicity, expressiveness, and code brevity.

In conclusion, Java is the most comparable to C# in its implementation of inheritance. There


are syntactic differences, but they are generally one and the same – at least when
compared to the other languages. They also benefit from deriving from the C family of
languages, so, understandably, both languages would share more in common.

5.0 References
1. Microsoft (2021 A) abstract (C# reference). Available at:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract
(Accessed 25th March 2023).

2. Microsoft (2021 B) Arrays (C# Programming Guide). Available at:


https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/ (Accessed 27th
March 2023).

3. Oracle (2022) Overriding and Hiding Methods. Available at:


https://docs.oracle.com/javase/tutorial/java/IandI/override.html (Accessed 28th March 2023).

4. Microsoft (2022) override (C# reference). Available at:


https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override
(Accessed 28th March 2023).

5. Hickey, R. (2022) Runtime Polymorphism. Available at:


https://clojure.org/about/runtime_polymorphism (Accessed 28th March 2023).

6. Agilliway (2022) Clojure in Practice: Functional Approaches and Implementation Strategies.


Available at:
https://agiliway.com/clojure-in-practice-functional-approaches-and-implementation-strategie
s/ (Accessed 28th March 2023).

7. Nikolov, I. (2018) Scala Design Patterns - Second Edition. Traits & Mixin Compositions –
Multiple Inheritances. Available at:
https://learning.oreilly.com/library/view/scala-design-patterns/9781788471305/ (Accessed
29th March 2023).

8
8. Scala (n.d.) Traits – Tour of Scala. Scala Documentation. Available at:
https://docs.scala-lang.org/tour/traits.html (Accessed 30th March 2023).

9. Cult3 (2015) Working with Mixins in Ruby. Available at:


https://culttt.com/2015/07/08/working-with-mixins-in-ruby (Accessed 1st April).

10. Kolleger, E. (2017) Inheritance versus Composition In Ruby. Medium. Available at:
https://medium.com/@MinimalGhost/inheritance-versus-composition-in-ruby-4ff52beb5e86
(Accessed 1st April 2023).

11. Draper, J. (2019) Static Typing in Ruby with a side of Sorbet. Heroku. Available at:
https://blog.heroku.com/static-typing-ruby-with-sorbet (Accessed 1st April 2023).

12. Ravindra, A (2022) Enterprise Application Development with C# 10 and .NET 6 - Second
Edition. Packt. Available at:
https://www.packtpub.com/product/enterprise-application-development-with-c-10-and-net-
6-second-edition/9781803232973 (accessed 4.19.23).

You might also like