December Code

C# inheritance and polymorphism interview questions, with answers

C# inheritance looks like Java's until the details: methods are not virtual unless you say so, a method can hide another with new instead of overriding it, interfaces can carry default implementations, and a class can implement two interfaces that clash. Interviewers use exactly those details to see whether you know the language or only the vocabulary. The general OOP questions are answered on the OOP pages; this page takes the C# ones, each with code that was compiled and run on .NET 10.

The questions start with the keywords that control overriding, move through abstract classes, interfaces and access, and end with type tests and pattern matching. Then take the free C# diagnostic — ten questions across every C# topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.What do virtual, override and sealed mean in C#?

    In short: virtual allows a method to be overridden, override replaces it for derived objects, and sealed stops further overriding of a method or any inheritance from a class.

    C# methods are non-virtual by default, so a derived class can change what a call through a base reference does only when the base method is virtual or abstract and the derived method says override, as Circle does below. A derived method with the same signature but no override hides the base method instead, which the compiler warns about unless new marks the hiding as intended. sealed override lets a class override a method while forbidding its own subclasses to override it again, and a sealed class cannot be inherited at all, which the last line shows. Non-virtual by default was a deliberate design choice: the author of a base class decides what may be replaced, which protects its invariants and lets calls be bound directly.

    Shape s = new Circle();
    Console.WriteLine(s.Name());
    
    class Shape
    {
        public virtual string
            Name() => "shape";
    }
    class Circle : Shape
    {
        public override string
            Name() => "circle";
    }
    sealed class Final { }
    class More : Final { }
    // does not compile
    // circle
  2. 2.What is the difference between an abstract class and an interface in C#?

    In short: An abstract class can hold state, constructors and any access level, and a class extends only one; an interface declares a contract, may have default methods since C# 8, and a class implements many.

    An abstract class is a partial implementation: it can have fields, constructors, protected members and a mix of abstract and concrete methods, as Shape below does, and single inheritance limits a class to one base. An interface declares what an implementing type can do: it has no instance fields or constructors, and a class or struct can implement any number of them. C# 8 blurred the line by allowing interfaces to carry default method implementations, static members and private helpers, mainly so that a library can add a method to a published interface without breaking existing implementations. Choose an abstract class for a family of types sharing code and state, and an interface for a capability many unrelated types can have.

    Shape s = new Square(3);
    Console.WriteLine(
        s.Describe());
    
    abstract class Shape
    {
        public abstract double
            Area();
        public string Describe() =>
            $"area {Area()}";
    }
    class Square(double side)
        : Shape
    {
        public override double
            Area() => side * side;
    }
    // area 9
  3. 3.How does a derived C# class call its base class's constructor and methods?

    In short: Use base(...) after the derived constructor's parameter list to choose the base constructor, and base.Method() inside a member to call the base version of an overridden method.

    A derived constructor always runs a base constructor first. Writing : base(args) after its parameter list, or naming the base's arguments in a primary constructor's base list, as Manager does below, chooses which one and with what arguments; without it the compiler calls the base's parameterless constructor, and if there is none, the derived constructor does not compile. Inside an override, base.Method() calls the version the method replaced, which lets an override extend behaviour rather than rewrite it. this(...) chains to another constructor of the same class instead. One difference from Java: a C# class's field initialisers run before the base constructor, so they are already set if the base constructor calls a virtual method.

    var m = new Manager("Meera");
    Console.WriteLine(m.Title());
    
    class Employee(string name)
    {
        public virtual string
            Title() => name;
    }
    class Manager(string name)
        : Employee(name)
    {
        public override string
            Title() => base.Title()
            + " (mgr)";
    }
    // Meera (mgr)
  4. 4.What is explicit interface implementation in C#?

    In short: Implementing a member as IName.Member makes it callable only through that interface, which resolves clashes between interfaces and keeps the member off the class's public surface.

    When a class implements two interfaces that declare a member with the same signature but different meanings, one method cannot serve both. Writing the member with the interface's name, string IPrint.Title() => ..., implements it explicitly: it takes no access modifier, it cannot be called through a variable of the class type, and it runs only through a reference of that interface type, as below. The same technique hides members that exist only to satisfy an interface, which is how the generic collections keep the old non-generic IEnumerable.GetEnumerator out of sight. A caller who needs an explicitly implemented member converts to the interface first.

    var r = new Report();
    IPrint p = r;
    IExport e = r;
    Console.WriteLine(p.Title());
    Console.WriteLine(e.Title());
    
    interface IPrint
    {
        string Title();
    }
    interface IExport
    {
        string Title();
    }
    class Report : IPrint, IExport
    {
        string IPrint.Title() =>
            "printed";
        string IExport.Title() =>
            "exported";
    }
    // printed
    // exported
  5. 5.What are C#'s access modifiers, including protected internal and private protected?

    In short: public, private, protected and internal, plus two combinations: protected internal means protected or internal, and private protected means protected and internal.

    private members are visible only inside their type, and private is the default for members, which is why count below cannot be used from outside; protected adds derived classes; internal means any code in the same assembly; public means everyone. The combinations are where interviews probe: protected internal is the union, so derived classes anywhere and all code in the same assembly can use the member, while private protected, added in C# 7.2, is the intersection, so only derived classes inside the same assembly can. Top-level types default to internal, which is why a class written without a modifier cannot be used from another project. C# 11 added file, which limits a type to the source file that declares it.

    var c = new Counter();
    c.Add();
    Console.WriteLine(c.Value);
    c.count = 5;
    // does not compile
    
    class Counter
    {
        int count;
        public int Value => count;
        public void Add()
            => count++;
    }
    // 1
  6. 6.What is the difference between is, as and a cast in C#?

    In short: A cast throws InvalidCastException when the object is the wrong type, as returns null instead, and is tests the type, and with a pattern also declares a typed variable.

    (Dog)animal converts or fails loudly with InvalidCastException. animal as Dog returns null for a mismatch, so it works only with reference and nullable types, and the result must be checked. animal is Dog tests without converting, and since C# 7 the pattern form animal is Dog d tests and assigns in one step, so d is definitely assigned inside the if. Patterns have grown into a small language: type patterns, property patterns such as shape is Circle { Radius: > 10 }, relational patterns, and switch expressions that branch on the type, as below. The general advice is to prefer a virtual method to type tests, and when a test is needed, to use an is pattern or a switch expression rather than as followed by a null check.

    object[] xs =
        { 4, "four", 4.5 };
    foreach (var x in xs)
    {
        object r = x switch
        {
            int n => n * 2,
            string s => s.Length,
            _ => "other"
        };
        Console.WriteLine(r);
    }
    // 8
    // 4
    // other

How the diagnostic asks it

One question from the C# bank, exactly as a sitting would show it. The bank has 3 on inheritance & polymorphism and 30 across C#.

Inheritance & Polymorphism · mediumCSHARP-015

What does this C# code print?

Base b = new Derived();
Console.WriteLine($"{b.A()} {b.B()}");

class Base {
    public virtual int A() => 1;
    public virtual int B() => 1;
}
class Derived : Base {
    public override int A() => 2;
    public new int B() => 2;
}
  1. 12 1correct
  2. 22 2
  3. 31 1
  4. 41 2

Both of Base's methods are virtual, and a call through b dispatches on the object, a Derived. override replaces Base's A in the virtual dispatch, so b.A() runs Derived's version: 2. new does something different: it declares a second, unrelated B in Derived that hides the inherited name without overriding it, so the virtual B still resolves to Base's version, and b.B() returns 1. The output is 2 1. 2 2 treats new as if it were override. 1 1 assumes a call through a Base variable never reaches Derived, which is exactly what virtual and override exist to do. 1 2 reverses the two keywords. Called through a Derived variable, both would run Derived's versions: 2 2.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 C# questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated