Features of C# Programming

Posted in /  

Features of C# Programming
paritoshlouhan

Paritosh Louhan
Last updated on December 21, 2024

    C# is a modern programming language derived from the concepts of C++ and Java that is widely used for web, mobile, and desktop application development. The success of C# stems from its numerous features that aid in the development of safe, scalable, and large-scale applications. Garbage collection, automated memory management, multithreading, scalability and many more features are included. We'll talk about some important C# features in this article.

    Object-Oriented Features of C#

    C# is an object-oriented programming language , which means C# programs are structured into objects and classes. It improves the readability, customization, and debugging of the software. Wrapping your code in the form of classes and objects is always a smart idea. Let's have a look at some of the most useful elements of object-oriented programming that you may use in your apps.

    Objects

    A person, automobile, or other real-world entity is an object. To put it in another way, an object is a thing that has a state and some functions.

    The term " state " refers to variables or data, and " function " refers to the operations that an object does. A runtime entity is one that is formed during runtime (not at compile time). An object is an instance of a class, and a class is nothing more than a blueprint. Objects can be used to access all members of the class. The ‘ new ’ keyword in C# can be used to construct the object. Following the ‘new’ keyword, you must specify the class to which that object belongs, followed by parenthesis. Consider the following example, which produces a “Person” object of the “Human” class:

    using System;
    namespace Application {
       class Human {
     
       // member variables
       int hands;
     
       public void InitializeHands() {
          hands = 2;    
       }
       public void Run() {
          Console.WriteLine("Running");
       }
       }
       class App {
       static void Main(string[] args) {
          Human person = new Human();   //new object
          Console.ReadLine();
       }
       }
    }

    Classes

    When you create a class, you're essentially creating a blueprint for a data type. This does not define any data, but it does define the meaning of the class name. That is, what a class object is made up of and what operations may be performed on it. A class's instances are objects. Members of a class are the methods and variables that make up the class. The syntax for writing class and its members is as follows:

    <access specifier> class  ClassName {
       <access specifier> <data type> MemberVariable1;
       <access specifier> <data type> MemberVariable2;
       ...
       <access specifier> <data type> MemberVariableN;
       <access specifier> <return type> Method1(parameters) {
        
       }
       <access specifier> <return type> Method2(parameters) {
         
       }
       ...
       <access specifier> <return type> MethodN(parameters) {
         
       }
    }

    Don't worry if access specifiers don't make sense to you right now. Later, we'll go through all forms of access specifiers. But first, let's look at an example to see how classes and members work. Let's use the same "Human" class we discussed earlier as an example:

    namespace Application {
       class Human {
     
       // member variables
       int hands;
     
       public void InitializeHands() {
          hands = 2;    
       }
       public void Run() {
          Console.WriteLine("Running");
       }
       }
       class App {
       static void Main(string[] args) {
          Human person = new Human();   //new object
                person.InitiazeHands()     
                person.Run();                //calling method
          Console.ReadLine();
       }
       }
    }

    In the above example, we created a single member variable “hands” that is set to 2 for each individual. We also have a member function called "Run" that when called outputs "Running." The member functions can be called using the syntax given in the code above. Members of classes reflect their data and behaviour. All members declared in the class, as well as all members (excluding constructors and finalizers) defined in all classes in its inheritance structure, make up a class's members. Private members in base classes are inherited, but derived classes cannot access them. Let's look at the 3 most common sorts of access specifiers; public, private, and protected.

    1. public : Any other code in the same class or another class can access it.

    2. private : Only code in the same class or struct can access a private type or member.

    3. protected: Only code from the same class, or a class that extends it, can access the type or member. The access modifiers are usually used to implement “encapsulation.” It is the process of ensuring that "sensitive" information is kept hidden from users. Declaring fields as private accomplishes this. The following code throws an error because we are trying to access the private member of another class:

    using System;
    
    namespace Application {
       class Test {
     
     //private member
       private int variable=2;
     
     
       }
       class App {
       static void Main(string[] args) {
          Test t = new Test();   //new object
          Console.WriteLine(t.variable);
          Console.ReadLine();
       }
       }
    }

    Inheritance

    In object-oriented programming, inheritance refers to the process of basing an object (prototype-based inheritance) or class (class-based inheritance) on another object or class while maintaining identical implementation. Deriving new classes (sub-classes) from existing ones, such as the superclass or base class, and then building them into a hierarchy of classes is also defined as inheritance. The relationship of objects and classes generated using inheritance forms a directed graph . Inheritance is helpful in code reusability and avoids writing the same block of code again and again. In the figures below, if A, B and C are 3 classes, then: Source: Wikipedia Source: Wikipedia Source: Wikipedia Let’s look at an example to derive a child class from the base class and then access the members of the base class:

    using System;
    
    namespace Application {
       class BaseClass {
     
     //private member
       public int variable=2;
     
     
       }
       class ChildClass: BaseClass{
        public int variable2=4;
       }
       
       class App {
       static void Main(string[] args) {
          ChildClass child = new ChildClass();   //new object
          Console.WriteLine("The value of variable of base class is");
          Console.WriteLine(child.variable);
          Console.ReadLine();
       }
       }
    }

    Output

    The value of a variable of the base class is 2 To avoid a class from inheriting child classes, you can use the “ sealed ” keyword with the class.

    The following code gives an error since we created a child class from a sealed class:

    using System;
    
    namespace Application {
       sealed class BaseClass {
     
    
       public int variable=2;
     
     
       }
       class ChildClass: BaseClass{
        public int variable2=4;
       }
       
       class App {
       static void Main(string[] args) {
          ChildClass child = new ChildClass();   //new object
          Console.WriteLine("The value of variable of base class is");
          Console.WriteLine(child.variable);
          Console.ReadLine();
       }
       }
    }

    Output: main.cs(11,10): error CS0509: `Application.ChildClass': cannot derive from sealed type `Application.BaseClass'

    Polymorphism

    Polymorphism refers to the fact that something exists in multiple forms. In simple terms, it is the ability of an object to exhibit different properties. A real-life example of polymorphism is of a person who is a father, a spouse, and a worker; all at the same time. As a result, the same person performs different roles in different settings. Let’s see polymorphism using an example:

    using System;
    
    namespace Application {
       class BaseClass {
     
     
       public void Run(){
           Console.WriteLine("Runs");
       }
     
     
       }
       class ChildClass: BaseClass{
        public void Run(){
           Console.WriteLine("Runs slow");
       }
       }
       
       class App {
       static void Main(string[] args) {
          BaseClass child = new ChildClass();
          child.Run();
          Console.ReadLine();
       }
       }
    }

    Output: Runs In the preceding example, we generated two classes, each of which has the same sort of member function that exists in many forms (polymorphism).

    There is one problem with this. When we create an object of a derived class, we must call the member function of the same class. However, the member function of the base class is used here. We can use " runtime polymorphism " to avoid this. We can declare the base class member function as "virtual" and apply the “override” keyword before the name of the child class function in runtime polymorphism. This will take care of our problem as shown below:

    using System;
    
    namespace Application {
       class BaseClass {
     
     
       public virtual void Run(){
           Console.WriteLine("Runs");
       }
     
     
       }
       class ChildClass: BaseClass{
        public override void Run(){
           Console.WriteLine("Runs slow");
       }
       }
       
       class App {
       static void Main(string[] args) {
          BaseClass child = new ChildClass();
          child.Run();
          Console.ReadLine();
       }
       }
    }

    Output: Runs slow

    Abstraction

    Data abstraction is the process of hiding some details from the user and only displaying what they need to know. Abstract classes or “ interfaces ” can be used to accomplish abstraction. Methods and classes can be made abstract by using the abstract keyword in C#. An abstract class is a type that can't be utilized to make objects (to access it, it must be inherited from another class). It is only possible to utilize an abstract method in an abstract class, and it does not have a body. The derived class provides the body to abstract methods. Let’s look at an example to see the demonstration of abstraction:

    using System;
    
    namespace Application {
    // Abstract class
    abstract class BaseClass
    {
      // Abstract method (does not have a body)
      public abstract void Run();
      // Regular method
      public void Walk()
      {
        Console.WriteLine("Walking");
      }
    }
    
    // Derived class (inherit from BaseClass)
    class ChildClass : BaseClass
    {
      public override void Run()
      {
        Console.WriteLine("Runs slowly");
      }
    }
    
    class Program
    {
      static void Main(string[] args)
      {
        ChildClass child = new ChildClass(); // Create an object
        child.Run();  // Call the abstract method
        child.Walk();  // Call the regular method
      }
    } 
    }

    Output:

    Runs slowly
    Walking

    Interfaces

    In C#, an interface is a class's blueprint. It's similar to an abstract class in that all of the methods declared within the interface are abstract methods. It is not capable of having a method body or being instantiated. An interface is used to achieve multiple inheritances, which isn't possible with normal classes in C#. Because it can't have a method body, it's utilized to accomplish complete abstraction. The classes can be made fully abstracted by using the “ interface ” keyword. It is a good practice to start the class names with ‘I’ while using the interface (for example, IBaseClass). Try implementing the same example that we discussed above using an interface and witness abstraction in action.

    Type Safety in C#

    The degree to which a programming language discourages or avoids type errors is known as type safety in computer science. The behaviours that a programming language classifies as type errors are frequently the result of attempting to conduct operations on items that aren't of the correct data type. Typesafe means that an object can't get into the memory of another object. To better comprehend the notion of type safety, consider the following example:

    public class A {
       public int Prop1{ get; set;}
    }
    
    public class B {
       public int Prop1{get;set;}
       public int Prop2{get;set;}
    }
    
    A obj = new A();

    You will no longer be able to cast your object to the second class i.e. B. Because of type safety in C#. If you try to cast it, a compile-time error will occur.

    Component-Oriented

    Component-oriented programming is a method of creating programs by integrating pre-existing and new components. It is analogous to how automobiles are constructed from various prebuilt parts. Software components are self-contained, self-describing functional packages that have type definitions that expose both behavior and data. The concepts of properties, methods, events, and attributes (or metadata) in C# promote component-oriented programming, allowing assemblies to be self-contained and self-describing components of functionality.

    Multithreading in C#

    Multithreading is one of the best C# features. The execution route of a program is defined as a thread. Each thread defines a distinct control flow. If your application requires complex and time-consuming tasks, it is typically beneficial to have multiple execution routes or threads, each of which performs a certain task. Threads are a lightweight method of production. The implementation of concurrent programming by modern operating systems is a common example of thread utilization. The use of threads reduces wastage of CPU cycles and improves application efficiency.

    So far, we've written programs in which a single thread runs as a single process, which is the application's running instance. However, the application can only do one thing at a time in this manner. It could be broken into smaller threads to allow it to perform multiple tasks at once. In C#, the “ System.Threading.Thread” class is used to manipulate threads. In a multithreaded program, it allows you to create and access individual threads. The main thread is the first one to be executed in a process. The main thread is automatically formed when a program starts execution. The child threads of the main thread are those created with the Thread class. You can access a thread using the “ CurrentThread” property of the Thread class. This is demonstrated below:

    using System;
    using System.Threading;
    
    namespace MultithreadingApplication {
       class MainThreadProgram {
          static void Main(string[] args) {
             Thread t = Thread.CurrentThread;
             t.Name = "Main Thread";
             
             Console.WriteLine("This is {0}", t.Name);
             Console.ReadKey();
          }
       }
    }

    Output: This is Main Thread

    Collections in C#

    Collection classes are specialized classes for data storage and retrieval. Stacks, queues, lists, arrays and hash tables are all supported by these classes. The interfaces of most collection classes are the same. Collection classes are used for a variety of things, including dynamically allocating memory to elements, retrieving a list of items based on an index, storing elements in an organized way and efficiently accessing them. They are divided into two categories, namely generic and non-generic. The “ System.Collections ” namespace contains the non-generic collection types and the “ System.Collections.Generic” namespace includes the generic collection types.

    1. Generic collections

    It contains interfaces and classes that describe generic collections, which allow users to design strongly typed collections that are safer and more efficient than non-generic strongly typed collections. Let’s create some generic collections in C#. The letter “ T ” denotes some data type in the following examples:

    List<T>

    It's a dynamic array that's equivalent to the non-generic ArrayList class in terms of behavior.

    Queue<T>

    Queue stores data in the first in first out (FIFO) manner.

    Stack<T>

    Stacks store data in the last in first out (LIFO) manner.

    HashSet<T>

    HashSet is an unordered collection of unique elements. It prevents duplicates from being inserted into the collection.

    LinkedList<T>

    It allows faster inserting and removing of elements. Let’s look at one example of List collection to see the working of generic collections:

    using System;
    using System.Collections.Generic;
      
    class App {
      
        // Main Method
        public static void Main(String[] args)
        {
      
            // Creating a List of integers
            List<int> list = new List<int>();
      
            // adding items in mylist
            for (int j = 0; j < 5; j++) {
                list.Add(j);
            }
      
            // Displaying items of mylist
            foreach(int item in list)
            {
                Console.WriteLine(item);
            }
        }
    }

    Output: 0 1 2 3 4

    2. Non-generic collections

    These are general-purpose data structures that use object references to handle any sort of object, but not in a type-safe manner.

    The classes in the "System.Collections" namespace used extensively are mentioned below:

    ArrayList

    It's a dynamic array, which implies the size of the array isn't fixed and can change over time.

    Hashtable

    It is a collection of key-and-value pairs that are grouped according to the key's hash code.

    Queue

    It's a collection of objects where the first in, first out rule applies. When you need first-in, first-out access to items, this is the class to utilize.

    Stack

    It's a data structure that's linear. It follows the LIFO (Last In, First Out) pattern. Let’s look at one example to implement a queue:

    using System;
    using System.Collections;
    
    class App {
    
     public static void Main()
     {
    
     // Creating a Queue
     Queue q = new Queue();
    
     // Inserting the elements into the Queue
     q.Enqueue("A");
     q.Enqueue("B");
           q.Enqueue("C");
     q.Enqueue("D");
    
    
     Console.Write("Total number of elements present in the Queue are: ");
    
     Console.WriteLine(q.Count);
    
     }
    }

    Output: Total number of elements present in the Queue are: 4

    Conclusion

    We discussed some of the key C# language features that you can utilize to create safe and efficient software applications on a wide scale. C# has handy tools and data structures that can be widely used in your projects. That’s why C# is widely employed in the development of large-scale applications.

    People are also reading:

    Leave a Comment on this Post

    0 Comments