Top C# Interview Questions(2005)

author image Hirely
at 06 Jan, 2025

Question: What are circular references in C#?

Answer:

A circular reference in C# occurs when two or more objects reference each other, creating a loop of references. This means that Object A references Object B, and Object B references Object A, directly or indirectly. Circular references are typically problematic because they can prevent the garbage collector from reclaiming memory, leading to memory leaks if not handled properly.

Example of a Circular Reference:

class A
{
    public B BInstance { get; set; }
}

class B
{
    public A AInstance { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        B b = new B();

        a.BInstance = b;  // A references B
        b.AInstance = a;  // B references A
    }
}
  • In this example, class A holds a reference to an instance of class B, and class B holds a reference to an instance of class A. This creates a circular reference.

Why are Circular References Problematic?

Circular references can create several issues in C# programs, primarily related to garbage collection and memory management.

  1. Memory Leaks:

    • In .NET, garbage collection (GC) is responsible for reclaiming memory from objects that are no longer in use. However, if two objects reference each other in a circular manner, the garbage collector might not be able to determine that these objects are unreachable.
    • Since they reference each other, they are still considered “in use” by the garbage collector, even though they may no longer be needed, leading to a memory leak.
  2. Increased Complexity:

    • Circular references make the management of objects more complex because objects are not easily freed. This can lead to bugs and performance issues, particularly in larger applications.

How Does C# Handle Circular References?

C# and the .NET runtime (CLR) have certain mechanisms to deal with circular references:

  1. Garbage Collection (GC):

    • The .NET garbage collector can detect and break circular references in reference types (like classes). However, it’s not perfect, and in complex cases, circular references may not always be detected.
    • In general, if the objects involved in the circular reference are unreachable from the root of the object graph (e.g., through local variables, static members, or other active references), the garbage collector can still eventually clean them up.
  2. Weak References:

    • In some scenarios, using weak references can help manage circular dependencies. A weak reference allows an object to be collected by the garbage collector even if it is still referenced by other objects. This can help mitigate circular reference problems when you don’t want to prevent an object from being garbage collected.
  3. Event Handlers:

    • One common cause of circular references in C# is the subscription of event handlers. If an object subscribes to events of another object, and the other object also holds a reference to the first one (perhaps indirectly through events), a circular reference can be formed.
    • In such cases, it’s important to unsubscribe from events when they are no longer needed to avoid creating circular references.

How to Prevent Circular References?

  1. Refactor Design:

    • One way to avoid circular references is to refactor the object design. Instead of creating direct references between objects, you can use patterns like Dependency Injection, Observer, or Mediator to decouple dependencies.
  2. Use IDisposable to Break References:

    • If you have objects that are involved in circular references and manage resources, implement the IDisposable interface and ensure that you manually break references between the objects when they are no longer needed.
    • For example, in the case of event handlers, unsubscribe from events when you’re done with the object.
  3. Weak References:

    • For some cases, you may want to use weak references, especially when caching or handling objects that are part of a large graph and don’t need to prevent garbage collection. The WeakReference class in .NET allows you to keep a reference to an object without preventing its collection.
  4. Event Unsubscription:

    • Always ensure that when objects subscribe to events, they unsubscribe when no longer needed. This is especially important for long-lived objects.

Example: Circular Reference with Event Handlers

class A
{
    public event EventHandler OnChange;

    public void TriggerEvent()
    {
        OnChange?.Invoke(this, EventArgs.Empty);
    }
}

class B
{
    public A AInstance { get; set; }

    public void HandleEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Event handled in B.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        B b = new B();
        a.OnChange += b.HandleEvent;  // A references B via event handler
        b.AInstance = a;              // B references A
        a.TriggerEvent();             // Triggers event, calling B's handler
    }
}
  • In this case, the circular reference is caused by the event handler: A references B through the event, and B references A through the AInstance property.
  • Solution: Make sure that you unsubscribe from events when no longer needed, or use weak references if appropriate.

Summary:

  • Circular references occur when two or more objects reference each other in a loop.
  • They can lead to memory leaks if the garbage collector cannot detect them as unreachable.
  • C#’s garbage collector generally handles circular references, but it’s important to manage memory properly by unsubscribing from events, using IDisposable, and considering weak references.
  • Preventing circular references often involves careful design, event unsubscription, and considering alternatives like dependency injection or weak references.

Question: What are partial classes in C#?

Answer:

A partial class in C# is a class definition that can be split across multiple files. The partial keyword allows you to define a class in more than one file, which is particularly useful in scenarios where the class is large or generated automatically by tools, such as a designer file for Windows Forms or WPF.

When using partial classes, the compiler treats all the separate parts as a single class during the compilation process. This allows you to organize and manage large classes more easily, especially in projects where different developers are working on different parts of the class.

Key Features of Partial Classes:

  1. Multiple File Definitions: A class marked as partial can be defined in multiple files, and the compiler combines them into a single class when compiling the code.

  2. Separation of Concerns: Partial classes are often used to separate different concerns within a class. For example, one part of the class might contain business logic, while another part might handle event handlers or UI-related functionality.

  3. Automatic Code Generation: Partial classes are commonly used in scenarios where some parts of the class are auto-generated by tools (e.g., designer files in WinForms or WPF). The auto-generated code is usually placed in a separate file, and developers can add their own code in another file without modifying the generated code.

  4. Compilation as One Class: Even though the class is split across multiple files, the compiler treats all the parts as one class. All members of the partial class must share the same namespace, and all parts of the class are compiled together.


Syntax of Partial Classes:

To define a partial class, you simply use the partial keyword in each part of the class definition.

Example:

File 1: Person.Part1.cs
public partial class Person
{
    public string Name { get; set; }
    
    public void Greet()
    {
        Console.WriteLine("Hello, my name is " + Name);
    }
}
File 2: Person.Part2.cs
public partial class Person
{
    public int Age { get; set; }
    
    public void CelebrateBirthday()
    {
        Age++;
        Console.WriteLine("Happy birthday! I'm now " + Age + " years old.");
    }
}
Main Program (Using the Partial Class):
class Program
{
    static void Main()
    {
        Person person = new Person();
        person.Name = "Alice";
        person.Age = 30;

        person.Greet();              // Output: Hello, my name is Alice
        person.CelebrateBirthday();  // Output: Happy birthday! I'm now 31 years old.
    }
}
  • Explanation: In the example, the class Person is split across two files. One file contains the Name property and the Greet() method, while the other file contains the Age property and the CelebrateBirthday() method. Despite being in separate files, the C# compiler combines them into one Person class.

When to Use Partial Classes:

  1. Auto-Generated Code:
    • Partial classes are often used when you have auto-generated code, such as from Visual Studio’s designer for Windows Forms or ASP.NET Web Forms. You can add custom functionality in one file without modifying the auto-generated code.
  2. Large Classes:
    • If a class is becoming too large or complex, you can split it into multiple files to improve readability and maintainability.
  3. Separation of Concerns:
    • For scenarios where different parts of the class should be logically separated, partial classes allow developers to separate different features or functionalities into different files.

Restrictions and Rules:

  1. Same Class Name:
    • All parts of the partial class must have the same class name and must be in the same namespace.
  2. Cannot Be in Different Assemblies:
    • The parts of the partial class must be within the same assembly. You cannot split a class across multiple assemblies.
  3. Access Modifiers:
    • You can use access modifiers (e.g., public, private, internal) in each part of the partial class, but they must be compatible. For example, one part can be public, and another part can be internal, but they cannot be contradictory.
  4. Static/Non-static:
    • You can declare one part of the class as static and another part as non-static, but care must be taken not to contradict the state or functionality of the class.

Partial Methods:

A partial method is a method declared in one part of a partial class that can optionally be implemented in another part of the class. If the method is not implemented, the compiler removes it, resulting in no runtime overhead.

Example of Partial Method:

File 1: MyClass.Part1.cs
public partial class MyClass
{
    partial void OnAction();  // Declaration of partial method
}
File 2: MyClass.Part2.cs
public partial class MyClass
{
    partial void OnAction()  // Implementation of partial method
    {
        Console.WriteLine("Action performed!");
    }
}
  • If the method OnAction() is not implemented in the second part, the compiler removes it, so there is no code for it to execute at runtime.

Summary:

  • Partial classes allow you to split the definition of a class across multiple files.
  • Useful for large classes or when parts of a class are auto-generated.
  • All parts of a partial class must have the same class name and namespace.
  • Partial methods allow you to declare methods that may or may not be implemented in other parts of the class.

Partial classes are particularly useful in scenarios where you want to separate code for organization, maintainability, or to keep auto-generated and custom code separate.

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