C%23 Coding Questions For Technical Interviews

author image Hirely
at 06 Jan, 2025

Question: What are C#‘s non-generic collection types?

Answer:

In C#, non-generic collection types are part of the System.Collections namespace. These collections are not type-safe, meaning they can hold elements of any data type. While they are still useful, generics (introduced in C# 2.0) offer better type safety, performance, and readability.

Non-generic collections are considered legacy collections and are generally used in older code or when working with APIs that do not support generics.


Key Non-Generic Collection Types in C#:

  1. ArrayList:

    • A resizable array that can store elements of any type (objects).
    • Allows duplicates and null values.
    • Provides methods to manipulate the collection (e.g., Add, Remove, Insert, etc.).
    • Can store elements of mixed types, but they are boxed into object type.
    ArrayList list = new ArrayList();
    list.Add(1);
    list.Add("string");
    list.Add(3.14);
  2. Hashtable:

    • A collection that stores key-value pairs (similar to a dictionary).
    • Both keys and values are of type object, meaning they can store any type.
    • The keys in a Hashtable must be unique.
    • The order of elements is not guaranteed (i.e., it is unordered).
    Hashtable hashtable = new Hashtable();
    hashtable["key1"] = "value1";
    hashtable[2] = 5;
  3. Queue:

    • A first-in, first-out (FIFO) collection.
    • It allows enqueueing (adding) and dequeueing (removing) elements.
    • Elements are stored as object type.
    • Useful for scenarios like processing tasks in order, such as event handling or job scheduling.
    Queue queue = new Queue();
    queue.Enqueue(1);
    queue.Enqueue("hello");
    queue.Enqueue(3.14);
  4. Stack:

    • A last-in, first-out (LIFO) collection.
    • Elements are stored as object type.
    • Supports pushing (adding) and popping (removing) elements.
    • Useful for scenarios like undo/redo functionality or parsing expressions.
    Stack stack = new Stack();
    stack.Push(10);
    stack.Push("world");
    stack.Push(3.14);
  5. SortedList:

    • A collection of key-value pairs that maintains the keys in sorted order.
    • Keys and values are of type object, allowing for a wide variety of types to be stored.
    • It behaves similarly to a Hashtable, but the keys are automatically sorted.
    SortedList sortedList = new SortedList();
    sortedList.Add("key1", "value1");
    sortedList.Add(2, "value2");
  6. DictionaryBase:

    • A base class for implementing custom dictionaries, typically used when you need to create a custom collection based on key-value pairs.
    • Provides functionality to work with key-value pairs but does not enforce any specific types on the keys or values.
    // Custom class derived from DictionaryBase
    public class CustomDictionary : DictionaryBase
    {
        // Custom logic here
    }

Comparison with Generic Collections:

  • Type Safety: Non-generic collections use object type, meaning all elements are boxed and unboxed at runtime, which can lead to performance hits and runtime errors if wrong types are inserted. In contrast, generic collections enforce type safety at compile time, preventing such errors.

  • Performance: Because non-generic collections store elements as object, each operation requires boxing and unboxing, which is less efficient compared to the direct use of strongly typed collections in generics (e.g., List<T>, Dictionary<TKey, TValue>).

  • Use Cases: Non-generic collections might be used when:

    • You need to work with legacy code.
    • You don’t know the types of elements at compile-time (e.g., working with mixed data types).
    • You need to work with APIs that do not support generics.

Example Code Using Non-Generic Collections:

using System;
using System.Collections;

public class NonGenericCollectionsExample
{
    public static void Main()
    {
        // ArrayList Example
        ArrayList arrayList = new ArrayList();
        arrayList.Add(1);
        arrayList.Add("hello");
        arrayList.Add(true);
        
        Console.WriteLine("ArrayList elements:");
        foreach (var item in arrayList)
        {
            Console.WriteLine(item);
        }

        // Hashtable Example
        Hashtable hashtable = new Hashtable();
        hashtable["name"] = "John";
        hashtable["age"] = 30;

        Console.WriteLine("\nHashtable elements:");
        foreach (DictionaryEntry entry in hashtable)
        {
            Console.WriteLine($"{entry.Key}: {entry.Value}");
        }

        // Queue Example
        Queue queue = new Queue();
        queue.Enqueue(100);
        queue.Enqueue("world");
        
        Console.WriteLine("\nQueue elements:");
        while (queue.Count > 0)
        {
            Console.WriteLine(queue.Dequeue());
        }

        // Stack Example
        Stack stack = new Stack();
        stack.Push("first");
        stack.Push("second");

        Console.WriteLine("\nStack elements:");
        while (stack.Count > 0)
        {
            Console.WriteLine(stack.Pop());
        }
    }
}

Summary:

  • Non-generic collections (like ArrayList, Hashtable, Queue, Stack, and SortedList) are part of C#‘s legacy System.Collections namespace, allowing you to store and manage data without enforcing type safety.
  • These collections use object as the base type, meaning you can store any type of object, but at the cost of performance (boxing/unboxing) and potential runtime errors if data types do not match.
  • Generic collections, introduced in C# 2.0, are preferred due to their type safety and performance benefits.

Question: What is the difference between managed and unmanaged code in C#?

Answer:

In C#, the distinction between managed and unmanaged code primarily revolves around memory management and how resources are handled during execution. The key difference lies in whether the .NET runtime (CLR) is involved in managing the code’s memory and execution, or if it is the responsibility of the developer to handle memory directly.


Managed Code:

  1. Definition:

    • Managed code is code that is executed under the control of the Common Language Runtime (CLR), which is the runtime environment of the .NET Framework or .NET Core.
  2. Memory Management:

    • Managed code benefits from automatic memory management through the garbage collector (GC). The CLR automatically handles the allocation and deallocation of memory, including the cleanup of unused objects, which reduces the likelihood of memory leaks or invalid memory access.
  3. Type Safety:

    • Managed code enforces type safety, ensuring that operations on variables or objects are valid according to their types. This prevents issues such as buffer overflows, type mismatches, and invalid memory access.
  4. Exception Handling:

    • Managed code supports structured exception handling through the try, catch, finally blocks, which allows for cleaner and safer error management.
  5. Platform Independence:

    • Managed code is platform-independent because it is executed by the CLR. The CLR provides an abstraction over the underlying operating system and hardware, allowing the same code to run on different platforms (Windows, Linux, macOS) with the help of the .NET runtime.
  6. Example:

    • Code written in C# is considered managed code because it runs on the CLR and utilizes features like garbage collection and type safety.
    class ManagedCodeExample
    {
        public void HelloWorld()
        {
            Console.WriteLine("Hello, World!");
        }
    }

Unmanaged Code:

  1. Definition:

    • Unmanaged code is code that runs directly on the operating system and interacts directly with the hardware, without the assistance of the CLR. The memory and resource management for unmanaged code must be handled manually by the developer.
  2. Memory Management:

    • In unmanaged code, memory allocation and deallocation must be handled manually, usually with pointers and functions like malloc() and free() (in C/C++). There is no garbage collection; the programmer must ensure that memory is properly allocated, used, and freed.
  3. Type Safety:

    • Unmanaged code does not have the same type safety guarantees as managed code. As a result, there is a higher risk of issues like buffer overflows, memory corruption, and pointer errors because the developer is responsible for ensuring that data types and memory accesses are valid.
  4. Exception Handling:

    • Unmanaged code typically does not support structured exception handling (like try/catch in C#), and errors or exceptions are often handled using operating system-specific mechanisms or custom code.
  5. Platform Dependence:

    • Unmanaged code is generally platform-dependent. The code is typically written to run on a specific platform (e.g., Windows, Linux, etc.) and may need to be rewritten or modified to work on other platforms.
  6. Examples:

    • Code written in languages like C, C++, or Assembly is often unmanaged because it directly interacts with hardware and memory without the CLR.
    // C example of unmanaged code
    #include <stdio.h>
    
    int main() {
        printf("Hello, World!\n");
        return 0;
    }

Key Differences Between Managed and Unmanaged Code:

FeatureManaged CodeUnmanaged Code
Execution EnvironmentRuns under the control of the CLR (Common Language Runtime)Runs directly on the operating system, not under the CLR
Memory ManagementAutomatic memory management via the garbage collector (GC)Manual memory management, usually through pointers
Type SafetyStrong type safety enforced by the CLRNo inherent type safety, more prone to errors
Error HandlingStructured exception handling with try-catchError handling is typically OS-specific or custom
Platform IndependencePlatform-independent (runs on any OS with CLR support)Platform-dependent, usually tied to a specific OS
PerformanceGenerally slower due to overhead from garbage collection and CLRTypically faster due to direct access to system resources
SecurityMemory access is more secure due to type safety and GCMore prone to security vulnerabilities (e.g., buffer overflows)
Example LanguagesC#, VB.NET, F#C, C++, Assembly

When to Use Managed vs Unmanaged Code:

  • Managed Code:

    • Preferred in most modern applications, especially in business, web, desktop, and mobile applications, because it simplifies development with automatic memory management, security, and portability.
    • Used for .NET Framework and .NET Core applications.
    • Example: Writing a console application in C# or developing a web application using ASP.NET.
  • Unmanaged Code:

    • Preferred when performance is critical and direct interaction with hardware is required (e.g., for low-level system programming, device drivers, or embedded systems).
    • Used in legacy applications, operating systems, game engines, and scenarios where memory control is paramount.
    • Example: Writing a performance-critical component in C or interfacing directly with hardware or OS-specific APIs.

Interop Between Managed and Unmanaged Code:

In some cases, you may need to call unmanaged code from managed code, especially when interacting with native libraries or system resources. This is done using P/Invoke (Platform Invocation Services) in C#, which allows managed code to call unmanaged functions in dynamic link libraries (DLLs) or shared libraries.

  • Example of using P/Invoke in C# to call unmanaged code:

    using System;
    using System.Runtime.InteropServices;
    
    class Program
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
    
        static void Main()
        {
            MessageBox(IntPtr.Zero, "Hello, World!", "Managed-to-Unmanaged Call", 0);
        }
    }

In this example, C# code uses P/Invoke to call the unmanaged MessageBox function from the user32.dll library, which is part of Windows.


Conclusion:

  • Managed Code is run under the control of the CLR, benefits from automatic memory management, and is safer and easier to maintain.
  • Unmanaged Code runs directly on the operating system and offers more control over memory but requires manual memory management and is prone to errors.
  • Managed code is generally preferred for most application development in C#, but unmanaged code is still important in performance-critical scenarios or system-level programming.

Read More

If you can’t get enough from this article, Aihirely has plenty more related information, such as C# interview questions, C# interview experiences, and details about various C# job positions. Click here to check it out.

Related Posts

Trace Job opportunities

Hirely, your exclusive interview companion, empowers your competence and facilitates your interviews.

Get Started Now