Kickstart your career with best deals on top training courses NY10 Click to Copy

C-Sharp Interview Questions

C# is a widely used programming language in the IT industry. It is developed by Microsoft and mostly used for building the desktop applications. If you are looking for making your career in the software industry as a developer, these best interview questions in C sharp will be useful in cracking the interview. Below is a list of most asked C# interview questions and answers. These C# interview questions can be useful for freshers and experienced professionals to clear the interview. Here are the C# basic interview questions that will make you ready to face the interview.

  • 4.5/5 Rating
  • 20 Question(s)
  • 30 Mins of Read
  • 3277+ Reader(s)

Beginner

Compile time polymorphism means we will declare methods with same name but different signatures because of this we will perform different tasks with same method name. This compile time polymorphism also called as early binding or method overloading.

Example: 

public class MyClass  
{  
  public int Add(int a, int b)  
  { 
      return a + b;  
  } 
  
  public int Add(int a, int b, int c)  
  { 
      return a + b + c;  
  } 
} 

Run time polymorphism also called as late binding or method overriding or dynamic polymorphism. Run time polymorphism or method overriding means same method names with same signatures.  

In this run time polymorphism or method overriding we can override a method in base class by creating similar function in derived class this can be achieved by using inheritance principle and using “virtual & override” keywords.

Example: 

public class Bclass
{
public virtual void Sample1()
{
Console.WriteLine("Base Class");
}
}
public class DClass : Bclass
{
public override void Sample1()
{
Console.WriteLine("Derived Class");
}
}
class Program
{
static void Main(string[] args)
{
DClass objDc = new DClass();
objDc.Sample1();
Bclass objBc = new DClass();
objBc.Sample1();
}
}

ByVal means passing direct value to the method, which means that any change to that parameter that takes place inside that method have no affect on original data stored in argument variable.

ByRef

ByRef variables does not contains the data directly, it contains a reference to its data, so if the value is changed inside the method, then the same reflects in the argument variable.

DataReader Object in ADO.NET is a stream-based , forward-only, read-only retrieval of query results from the Data Sources , which do not update the data. The DataReader cannot be created directly from code, they can created only by calling the ExecuteReader method of a Command Object. 

SqlDataReader sqlReader = sqlCmd.ExecuteReader();

The DataReader Object provides a connection oriented data access to the Data Sources. A Connection Object can contain only one DataReader at a time and the connection in the DataReader remains open, also it cannot be used for any other purpose while data is being accessed. When we started to read from a DataReader it should always be open and positioned prior to the first record. The Read() method in the DataReader is used to read the rows from DataReader and it always moves forward to a new valid row, if any row exist . 

DataReader.Raed();

Below is the sample code on how to bind data to DataReader:

public partial class Form1 : Form

    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {

            string connetionString = null;

            SqlConnection sqlCnn ;

            SqlCommand sqlCmd ;

            string sql = null;

            connetionString = "Data Source=ServerName;Initial Catalog=DatabaseName;User ID=UserName;Password=Password";

            sql = "Your SQL Statement Here , like Select * from product";

 

            sqlCnn = new SqlConnection(connetionString);

            try

            {
                sqlCnn.Open();
                sqlCmd = new SqlCommand(sql, sqlCnn);
                SqlDataReader sqlReader = sqlCmd.ExecuteReader();
                while (sqlReader.Read())
                {
                    MessageBox.Show(sqlReader.GetValue(0) + " - " + sqlReader.GetValue(1) + " - " + sqlReader.GetValue(2));
                }
                sqlReader.Close();
                sqlCmd.Dispose();
                sqlCnn.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Can not open connection ! ");
            }
        }
    }

Class

A class is a user-defined blueprint or prototype from which objects are created. Basically, a class combines the fields and methods(member function which defines actions) into a single unit.

Example:

public class Author {  
// Data members of class 
public string name; 
public string language; 
public int article_no; 
public int improv_no; 
// Method of class 
public void Details(string name, string language, 
int article_no, int improv_no) 
{ 
this.name = name; 
this.language = language; 
this.article_no = article_no; 
this.improv_no = improv_no; 
Console.WriteLine("The name of the author is : " + name 
+ "\nThe name of language is : " + language 
+ "\nTotal number of article published "
+ article_no + "\nTotal number of Improvements:"
+" done by author is : " + improv_no); 
} 
}

Structure

A structure is a collection of variables of different data types under a single unit. It is almost similar to a class because both are user-defined data types and both hold a bunch of different data types.

// Defining structure 
public struct Car 
{ 
 
// Declaring different data types 
public string Brand; 
public string Model; 
public string Color; 
}

Difference between Class and Structure

Class
Structure
Classes are of reference types.
Structs are of value types
All the reference types are allocated on heap memory.
All the value types are allocated on stack memory.
Allocation of large reference type is cheaper than allocation of large value type.
Allocation and de-allocation is cheaper in value type as compare to reference type.
Class has limitless features.
Struct has limited features.
Classes can contain constructor or destructor.
Structure does not contain constructor or destructor.
Classes used new keyword for creating instances.
Struct can create an instance, without new keyword
A Class can inherit from another class
A Struct is not allowed to inherit from another struct or class
Function member of the class can be virtual or abstract.
Function member of the struct cannot be virtual or abstract.
Two variable of class can contain the reference of the same object and any operation on one variable can affect another variable
Each variable in struct contains its own copy of data(except in ref and out parameter variable) and any operation on one variable can not effect another variable.

Advanced

Abstract Class

An abstract class is a special kind of class that can be inherited but cannot be instantiated. For a class to be an abstract class, there should always be at least one abstract method should be present.

Interface

Interface is not a class, it is an entity that is defined by the work Interface. Like Abstract class we cannot create an instance of Interface. It has no implementation; only has the signature i.e. just the definition of the methods without the body. 

Advantage of Interface is that it provides a way for a class to be a part of two classes: one from inheritance hierarchy and one from the interface.

Difference between Interface and Abstract Class

Feature
Interface
Abstract class
Multiple inheritance
A class can inherit to multiple interfaces
A class may inherit from only one abstract class.
Default implementation
An interface only have method declaration, no definition.
An abstract class can have methods with complete definition or abstract methods that to be overriden in derived class
Access Modifiers
for interface everything is assumed as public
An abstract class have access modifiers for the subs, functions, properties
Homogeneity
Interfaces are better option when various implementations share the same method signature.
Abstract classes are better when various implementations are of the same kind and use common behaviour or status.
Adding functionality (Versioning)
Adding a new method to interface need to implemented in derived classes
while adding new method we have the option of providing default implementation and therefore all the existing code might work properly.
Fields and Constants
No fields can be defined
can have fields and constants defined

Garbage collector manages allocation and reclaiming of memory. GC (Garbage collector) makes a trip to the heap and collects all objects that are no longer used by the application and then makes them free from memory.

Each time you create a new object, the common language runtime allocates memory for the object from the managed heap. As long as address space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.

Garbage collection occurs when one of the following conditions is true:

  • The system has low physical memory. This is detected by either the low memory notification from the OS or low memory indicated by the host.
  • The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.
  • The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.

Managed Heap

After the garbage collector is initialized by the CLR, it allocates a segment of memory to store and manage objects. This memory is called the managed heap, as opposed to a native heap in the operating system.

When the garbage collector is triggered, it reclaims the memory occupied by dead objects, also compacts the live objects so that they are moved together and dead space is removed. This way it 

makes the heap smaller and ensure that allocated objects stay together on managed heap.

Based on object life heaps can be considered as accumulation of two heap: large object heap and small object heap.

Heap is managed by different 'Generations', it stores and handles long-lived and short-lived objects, see the below generations of Heap:

  • 0 Generation (Zero): This generation holds short-lived objects, e.g., Temporary objects. GC initiates garbage collection process frequently in this generation.
  • 1 Generation (One): This generation is the buffer between short-lived and long-lived objects.
  • 2 Generation (Two): This generation holds long-lived objects like a static and global variable, that needs to be persisted for a certain amount of time. Objects which are not collected in generation Zero, are then moved to generation 1, such objects are known as survivors, similarly objects which are not collected in generation One, are then moved to generation 2 and from there onwards objects remain in the same generation.

What happens during a garbage collection

Following phases are there of garbage collection:

  • Marking Phase: that finds and creates a list of all live objects.
  • Relocating Phase: that updates the references to the objects that will be compacted.
  • Compacting Phase: that reclaims the space occupied by the dead objects and compacts the surviving objects. It moves objects that have survived a garbage collection toward the older end of the segment.

Reflection is a process by which a computer program can monitor and modify its own structure and behavior. It is a way to explore the structure of assemblies at run time (classes, resources, methods). Reflection is the capability to find out the information about objects, metadata, and application details (assemblies) at run-time. We need to include System.Reflection namespace to perform reflections in C#.

Exmaple:

public class MyClass
{
    public virtual int Add(int numb1, int numb2)
    {             
        return numb1 + numb2;
    }
    public virtual int Subtract(int numb1, int numb2)
    {
        return numb1 - numb2;
    }
}
 
static void Main(string[] args)
{
    MyClass oMyClass = new MyClass();
    //Type information.
    Type oMyType = oMyClass.GetType();
    //Method information.
    MethodInfo oMyMethodInfo = oMyType.GetMethod("Subtract");
 
    Console.WriteLine("\nType information:" + oMyType.FullName);
    Console.WriteLine("\nMethod info:" + oMyMethodInfo.Name);            
    Console.Read();
} 

Why we need Reflection

We need reflection for the following purposes:

  • To view attribute information at run time
  • To view the structure of assemblies at run time (classes, resources, methods)
  • It allows dynamic/late binding to methods and properties
  • In serialization, it is used to serialize and de-serialize objects
  • In web service, it is used to create and consume SOAP messages and also to generate     WSDL
  • Debugging tools can use reflection to examine the state of an object

Func, Action and Predicate are define in C# 3.0 and these are generic inbuilt delegates.

Func Delegate

Func is generic delegate present in System namespace. It takes one or more input parameters and returns one out parameter. The last parameter is considered as a return value.

Func delegate type can include 0 to 16 input parameters of different types. It must have one return type. So return type is mandatory but input parameter is not.

Example1: Func delegate with two input parameters and one return value.

    Func func1 = DelegateClass.Add;  

    int value = func1(10, 20);  

    TParameter = 10,20;  

    TOutput = value = 30;   

Example 2: Func delegate with one input parameter and one return value.

    Func func2 = DelegateClass.GetValue;  

    int values = func2(30);  

    TParameter = 30  

    TOutput = 40;  

Example 3: Func delegate with zero input parameter and one return value.

    Func func3 = DelegateClass.GetResult;  

    intresultMulti = func3();  

    TParameter = Nothing  

    TOutput = 600;     

    Func with Anonymous methods:  

    Func func4= delegate(intx,int y){ return (x+y); };  

    int result = func4(2,3);      

    Func with Lambda expression:  

    Func func5= (intx,int y) => { return (x+y); };  

    int xx = func4(2,3);  

Action Delegate

Action is a generic delegate present in System namespace. It takes one or more input parameters and returns nothing.

So it does not return any value.

Example 1: Action delegate with two input parameters.

    Action action1=DelegateClass.ShowEmploye;  

    action1(30,"Rajesh");  

    TParameter = 30,Rajesh;  

    TOutput = Not available (No return value)  

Example 2: Action delegate with one input parameter.

    Action action2=DelegateClass.ShowMessage;  

    action2("Rajesh");  

    TParameter = “Rajesh”  

    TOutput = Not available  

Action delegate with Anonymous methods:

    Action action = delegate(String msg)  
    { 
        Console.WriteLine(msg);  
    }; 
    action("rajesh");  
    Action delegate with Lambda expression: Action action = (msg) = & gt;  
    { 
        Console.WriteLine(msg)  
    }; 
    action(“Rajesh”);  

Predicate Delegate

Predicate delegate is also inbuilt generic delegate and is present in System namespace.

It is used to verify certain criteria of method and returns output as Boolean, either True or False.

Predicate can be used with method, anonymous and lambda expression.

Example 1: Check String value is number using Predicate delegates. 

Predicate predicate = DelegateClass.IsNumeric;

bool number = predicate("1234");

Example 2: Predicate delegate using Anonymous method.

    Predicate predicate = delegate(string str)  
    { 
        double retNum;  
        bool isNum = Double.TryParse(Convert.ToString(str), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);  
        return isNum;  
    }; 
    bool found = predicate("12232");  

Example 3: Predicate delegate using lambda expression.

    Predicate predicate = (str) = & gt;  
    { 
        double retNum;  
        bool isNum = Double.TryParse(Convert.ToString(str), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);  
        return isNum;  
    }; 
    bool found = predicate("12232");

Stack

Stack is an array of memory.

  • It is a Last-in, First-out (LIFO) data structure.
  • Data can be added to and deleted only from the top of the stack.
  • Placing a data item at the top of the stack is called pushing the item onto the stack.
  • Deleting an item from the top of the stack is called popping the item from the stack.

Heap

The heap is an area of memory where chunks are allocated to store certain kinds of data objects. Unlike the stack, data can be stored and removed from the heap in any order. CLR’s garbage collector (GC) automatically cleans up orphaned heap objects when it determines that your code can no longer access them.

Difference between Stack and Heap Memory

Stack Memory
Heap Memory
Static memory allocation
Dynamic memory allocation
Variables allocated on stack are directly stored to memory
Variables allocated on Heap have their memory allocated at runtime
Variables cannot be re-sized
Variables can be re-sized
Very fast access
Relatively slow access
Variables stored on stack are visible only to the owner thread
Objects created on the heap are visible to all threads.
Stack is always reserved in LIFO order, the most recently reserved block is always the next block to be freed.
You can allocate a block and free it at any time.
The  moment stack space is exhausted, .NET runtime throws StackOverflowException.Memory
.NET runtime creates special thread that monitors allocation of heap space called Garbage Collector

Async-await is used to perform action asynchronously. It is required when there are long running activities, processing them synchronously may take long time and become blocker to web access. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes. 

The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in the .NET Framework. Asynchronous methods that you define by using the async keyword are referred to as async methods.

The following example shows an async method.

// Three things to note in the signature:  
//  - The method has an async modifier.   
//  - The return type is Task or Task<T>. (See "Return Types" section.)  
//    Here, it is Task<int> because the return statement returns an integer.  
//  - The method name ends in "Async."  
async Task<int> AccessTheWebAsync()  
{   
    // You need to add a reference to System.Net.Http to declare client.  
    HttpClient client = new HttpClient();  
  
    // GetStringAsync returns a Task<string>. That means that when you await the  
    // task you'll get a string (urlContents).  
    Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");  
  
    // You can do work here that doesn't rely on the string from GetStringAsync.  
    DoIndependentWork();  
  
    // The await operator suspends AccessTheWebAsync.  
    // - AccessTheWebAsync can't continue until getStringTask is complete.  
    // - Meanwhile, control returns to the caller of AccessTheWebAsync.  
    // - Control resumes here when getStringTask is complete.   
    // - The await operator then retrieves the string result from getStringTask.  
    string urlContents = await getStringTask;  
  
    // The return statement specifies an integer result.  
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.  
    return urlContents.Length;  
}

For a method to be async, it should have following characteristics:

  • The method signature includes an async modifier.
  • The return type is one of the following types:

    • if method have a return statement then Task<TResult>.
    • if no return statement  then Task.
  • The method usually includes at least one await expression.

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.

An async method typically returns a Task or a Task<TResult>. Inside an async method, an await operator is applied to a task that's returned from a call to another async method. You specify Task<TResult> as the return type if the method contains a return statement that specifies an operand of type TResult.

You use Task as the return type if the method has no return statement or has a return statement that doesn't return an operand. An async method can't declare in, ref or out parameters, but the method can call methods that have such parameters. Similarly, an async method can't return a value by reference, although it can call methods with ref return values

A private constructor is a special instance constructor. It is generally used in classes that contain static members only. If a class has one or more private constructors and no public constructors, other classes (except nested classes) cannot create instances of this class. For example:

class NLog
{
    // Private Constructor:
    private NLog() { }
 
    public static double e = Math.E;  //2.71828...
}
 

The declaration of the empty constructor prevents the automatic generation of a default constructor. 

Note that if you do not use an access modifier with the constructor it will still be private by default. 

Private constructors are used to prevent creating instances of a class when there are no instance fields or methods, such as the Math class, or when a method is called to obtain an instance of a class.

There are two ways to use the using keyword in C#. One is as a directive and the other is as a statement.

  1.  Using Directive
    Generally we use the using keyword to add namespaces in code-behind and class files. Then it makes available all the classes, interfaces and abstract classes and their methods and properties in the current page. Adding a namespace can be done in the following two ways: 

A.To allow the normal use of types in a namespace:

  1. using System.IO;   
  2. using System.Text;   

B.To create an alias for a namespace or a type. This is called a using alias directive. 

  1. using MyProject = TruckingApp.Services;

  1.  Using Statement

         This is another way to use the using keyword in C#. It plays a vital role in improving performance in Garbage Collection.

The using statement ensures that Dispose() is called even if an exception occurs when you are creating objects and calling methods, properties and so on. Dispose() is a method that is present in the IDisposable interface that helps to implement custom Garbage Collection. In other words if I am doing some database operation (Insert, Update, Delete) but somehow an exception occurs then here the using statement closes the connection automatically. No need to call the connection Close() method explicitly.

Another important factor is that it helps in Connection Pooling. Connection Pooling in .NET helps to eliminate the closing of a database connection multiple times. It sends the connection object to a pool for future use (next database call). The next time a database connection is called from your application the connection pool fetches the objects available in the pool. So it helps to improve the performance of the application. So when we use the using statement the controller sends the object to the connection pool automatically, there is no need to call the Close() and Dispose() methods explicitly

For Example:

    string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";  
       
    using (SqlConnection conn = new SqlConnection(connString))  
    { 
          SqlCommand cmd = conn.CreateCommand();  
          cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";  
          conn.Open();  
       
          using (SqlDataReader dr = cmd.ExecuteReader())  
          { 
             while (dr.Read())  
             Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));  
          } 
    } 

In order to maintain security and type safety, C# does not support pointer generally. But by using unsafe keyword we can define an unsafe context in which pointer can be used. The unsafe code or unmanaged code is a code block that uses a pointer variable. In the CLR, unsafe code is referred to as unverifiable code. In C#, the unsafe code is not necessarily dangerous. The CLR does not verify its safety. The CLR will only execute the unsafe code if it is within a fully trusted assembly. If we use unsafe code, it is our own responsibility to ensure that the code does not introduce security risks or pointer errors.

Unsafe code cannot be executed in an un-trusted environment. For example, we cannot run unsafe code directly from the Internet 

Some properties of unsafe codes are given bellow:

  • We can define Methods, types, and code blocks as unsafe
  • In some cases, unsafe code may increase the application’s performance by removing array bounds checks
  • Unsafe code is required in order to call native functions that require pointers
  • Using unsafe code brings security and stability risks
  • In order to compile unsafe code, the application must be compiled with /unsafe

Abstraction

 Abstraction means to show only the necessary details to the client of the object. Abstraction is the process of refining away all the unneeded/unimportant attributes of an object and keep only the characteristics best suitable for your domain. Abstraction means putting all the variables and methods in a class which are necessary.

Encapsulation 

Encapsulation is a strategy used as part of abstraction. Encapsulation refers to the state of objects - objects encapsulate their state and hide it from the outside; outside users of the class interact with it through its methods, but cannot access the classes state directly. So the class abstracts away the implementation details related to its state. It is a method for protecting data from unwanted access or alteration. Encapsulation is the mechanism by which Abstraction is implemented.

Difference between Abstraction and Encapsulation

Abstraction
Encapsulation
solves the problem in the design level.
solves the problem in the implementation level.
hiding the unwanted data and giving only relevant data.
hiding the code and data into a single unit to protect the data from outer world
set focus on the object instead of how it does it
hiding the internal details or mechanics of how an object does something

When we want to transport an object through network then we need to convert the object into a stream of bytes. Serialization is a process to convert a complex objects into stream of bytes for storage (database, file, cache, etc) or transfer. Its main purpose is to save the state of an object.
De-serialization is the reverse process of creating an object from a stream of bytes to their original form.

Types of Serialization:

Serialization is of following types:

  • Binary Serialization
    In this process all the public, private, read only members are serialized and convert into stream of bytes. This is used when we want a complete conversion of our objects. 
  • SOAP Serialization
    In this process only public members are converted into SOAP format. This is used in web services. 
  • XML Serialization
    In this process only public members are converted into XML. This is a custom serialization. Required namespaces: System.Xml, System.Xml.Serialization.

Serialization is used in the following purposes:

  • To pass an object from on application to another
  • In SOAP based web services
  • To transfer data through cross platforms, cross devices

The name itself describes that compiler knows about what kind of object it is, what are all the methods and properties it contains. As soon as you declared the object, .NET Intellisense will populate its methods and properties on click of the dot button

Common Examples:

  • ComboBox cboItems;
  • ListBox lstItems;

In the above examples, if we type the cboItem and place a dot followed by, it will automatically populate all the methods, events and properties of a combo box, because compiler already know it's an combobox.

Late Binding
The name itself describes that compiler does not know what kind of object it is, what are all the methods and properties it contains. You have to declare it as an object, later you need get the type of the object, methods that are stored in it. Everything will be known at the run time. 

Common Examples:

  • Object objItems;
  • objItems = CreateObject("DLL or Assembly name");

Here during the compile time, type of objItems is not determined. We are creating an object of a dll and assigning it to the objItems, so everything is determined at the run time. 

Composition

Like Inheritance gives us an 'is-a' relationship. Composition gives us a 'part-of' relationship. Composition is shown on a UML diagram.

If we were going to model a car, it would make sense to say that an engine is part-of a car. Within composition, the lifetime of the part (Engine) is managed by the whole (Car), in other words, when Car is destroyed, Engine is destroyed along with it. 

public class Engine
{
 . . . 
}
public class Car
{    Engine e = new Engine();    .......
}

As you can see in the example code above, Car manages the lifetime of Engine. 

Aggregation

As Inheritance gives us 'is-a' and Composition gives us 'part-of', Aggregation gives us a 'has-a' relationship. Within aggregation, the lifetime of the part is not managed by the whole. 

For Example:


Aggregation would make sense in this situation, as a Person 'has-a' Address. It wouldn't make sense to say that an Address is 'part-of' the Person, because it isn't. Consider it this way, if the person ceases to exist, does the address

So how do we express the concept of aggregation in C#? Well, it's a little different to composition. Consider the following code:

public class Address
{
 . . .
}
public class Person
{
     private Address address;
     public Person(Address address)
     {
         this.address = address;
     }
     . . .
}

Person would then be used as follows:

Address address = new Address();
Person person = new Person(address);

or

Person person = new Person( new Address() );

Here  Person does not manage the lifetime of Address. If Person is destroyed, the Address still exists.

Covariance and Contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

The following code demonstrates the difference between assignment compatibility, covariance, and contravariance.

// Assignment compatibility.   
string str = "test";  
// An object of a more derived type is assigned to an object of a less derived type.   
object obj = str;   

// Covariance.   

IEnumerable<string> strings = new List<string>();  
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable<object> objects = strings;  

// Contravariance.             

// Assume that the following method is in the class:   
// static void SetObject(object o) { }   
Action<object> actObject = SetObject;  
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action<string> actString = actObject;

Covariance and contravariance support for method groups allows for matching method signatures with delegate types. This enables you to assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type.

The following code example shows covariance and contravariance support for method groups.

static object GetObject() { return null; }  
static void SetObject(object obj) { }  
static string GetString() { return ""; }  
static void SetString(string str) { }    
static void Test()  
{  
    // Covariance. A delegate specifies a return type as object,  
    // but you can assign a method that returns a string.  
    Func<object> del = GetString;  
    // Contravariance. A delegate specifies a parameter type as string,  
    // but you can assign a method that takes an object.  
    Action<string> del2 = SetObject;  
}

In .NET Framework 4 or newer C# supports covariance and contravariance in generic interfaces and delegates and allows for implicit conversion of generic type parameters 

The following code example shows implicit reference conversion for generic interfaces.

IEnumerable<String> strings = new List<String>();  
IEnumerable<Object> objects = strings;

dynamic is a new static type that acts like a placeholder for a type not known until runtime. Once the dynamic object is declared, it is possible to call operations, get and set properties on it, even pass the dynamic instance pretty much as if it were any normal type.

The dynamic type enables the operations in which it occurs to bypass compile-time type checking. these operations are resolved at run time. Type dynamic behaves like type object in most circumstances. However, operations that contain expressions of type dynamic are not resolved or type checked by the compiler.

As part of the process, variables of type dynamic are compiled into variables of type object. Therefore, type dynamic exists only at compile time, not at run time.

The following example contrasts a variable of type dynamic to a variable of type object. To verify the type of each variable at compile time, place the mouse pointer over dyn or obj in the WriteLine statements. IntelliSense shows dynamic for dyn and object for obj.

class Program

{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;
 
        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
} 

Limitations of dynamic

  •   Extension methods cannot be called
  •   Anonymous functions (lambdas) cannot be called
  •   Because of the above, LINQ support is limited

Generics are the most powerful features introduced in C# 2.0. It is a type-safe data structure that allows us to write codes that works for any data types.

For example, by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations

// Declare the generic class.
public class GenericList<T>
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);
 
        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");
 
        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
} 

Stand-out to the potential employers with your C# knowledge 

The popularity of C# is increasing rapidly and it is playing a major role in the software industry. I hope these C sharp  interview questions help you face any kind kind of questions in the interview.

Set yourself up with C# programming interview questions to confront any interview confidently!

Description

C# is a widely used programming language in the IT industry. It is developed by Microsoft and mostly used for building the desktop applications. More recently C# is being used in developing the Windows 8/10 applications.

Based on the survey conducted by Stackoverflow, C# is known as the 4th most popular programming language, with 31% of the developers using it regularly. Since it's demand is rising in the market, it has become a necessity today to have a strong understanding of this language to land any dream job in the software industry.

If you are looking for making your career in the software industry as a developer, these best interview questions in C sharp will be useful in cracking the interview. Below is a list of most asked C# interview questions and answers. These C# interview questions can be useful for freshers and experienced professionals to clear the interview.

Here are the C# basic interview questions that will make you ready to face the interview.