354 .NET Developer Interview Questions & Answers

127 top • 51 Amazon • 43 Apple • 54 Google • 33 Meta • 46 Microsoft

.NET Developer icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

41. How do you create and use a custom exception?Language SpecificMedium

Question Details

Focus on naming, inheritance from Exception, and what extra context should be preserved for callers.

Short Interview Answer (30-60 seconds)

Create a class that inherits from Exception, give it a clear name, and keep any extra context the caller needs, such as an id, a code, or the original failure. Then throw that type and catch it where special handling is needed.

Detailed Explanation

See the Code while reading this explanation.

This question is asking how to make your own error class for a problem that needs a clearer name and more details than a normal message. You use it when one kind of failure needs special handling or better logging. The class can carry things like an order id, a code, or the original failure so the caller can understand what happened. The point is to make the problem easier to find, easier to log, and easier to handle in the right place.

Useful Questions to Ask the Interviewer
  1. Do you want an error code or record id on the exception?
  2. Should callers catch this type directly or wrap it again?
How do you create and use a custom exception? diagram
How to Explain It in an Interview

In C#, a custom exception is a class that inherits from Exception. The name should describe the failure clearly, such as OrderNotProcessedException or PaymentFailedException. Add only the details that callers really need, such as an order id, an error code, or another exception that caused the failure.

The main value is context. A plain message is often not enough for logs, support, or recovery logic. A custom type lets callers catch one specific failure and react in a focused way. If you wrap another error, pass it as innerException so the original cause is not lost.

Use a custom exception when the built in exception types do not describe the business problem well enough. Do not use one for every simple validation case. For bad input, ArgumentException or a similar built in type is often better. In production, keep the class small, keep the extra data readable, and preserve the original cause whenever you can.

Code
using System;

public sealed class OrderNotProcessedException : Exception
{
    public string OrderId { get; }

    public string ErrorCode { get; }

    public OrderNotProcessedException(string orderId, string errorCode, string message)
        : this(orderId, errorCode, message, null)
    {
    }

    public OrderNotProcessedException(string orderId, string errorCode, string message,
                                      Exception? innerException)
        : base(message, innerException)
    {
        // Store the context that callers need for logs and handling.
        OrderId = orderId;
        ErrorCode = errorCode;
    }
}

public static class Program
{
    public static void Main()
    {
        try
        {
            ProcessOrder("A100");
        }
        catch (OrderNotProcessedException ex)
        {
            // Catch the custom type so we can read the extra details directly.
            Console.WriteLine(ex.Message);
            Console.WriteLine(ex.OrderId);
            Console.WriteLine(ex.ErrorCode);
            Console.WriteLine(ex.InnerException?.Message);
        }
    }

    private static void ProcessOrder(string orderId)
    {
        try
        {
            throw new InvalidOperationException("The payment service rejected the request.");
        }
        catch (Exception ex)
        {
            // Wrap the lower level failure and keep the original cause.
            throw new OrderNotProcessedException(orderId, "PAYMENT_REJECTED",
                                                 "The order could not be completed.", ex);
        }
    }
}
Why Interviewers Ask This

The interviewer wants to see whether you can design a clear exception type, inherit from Exception correctly, and keep the extra context that helps callers log, debug, and handle the failure.

Common interview mistakes

A common mistake is to put all detail only in the message and leave callers to parse strings. Another mistake is to hide the original cause and lose the inner exception. A third mistake is to create a custom exception for simple validation when ArgumentException is enough. Keep the type focused and give it only the context that callers really need.

Interview tip

Start with the rule: inherit from Exception, use a clear name, add read only context fields, and pass the original error as innerException when you wrap another failure.

Interviewer may ask next
What happens if I catch Exception instead of the custom type?

Direct answer: The exception still reaches that catch block, but the caller loses the special meaning unless it checks the custom type. That matters because the extra fields are only useful when code handles that type directly or logs them. The tradeoff is that a broad catch is simpler, but it is less precise.

Should every custom exception include innerException?

Direct answer: It should include innerException whenever you are wrapping another failure. That matters because the original error stays visible for logs and debugging. The tradeoff is a little more constructor code, but much better root cause information. If there is no lower level failure, pass null or use a constructor without it.

42. What are delegates in C#?Language SpecificMedium

Question Details

Explain delegate types as type-safe callable references and how they enable callbacks and event patterns.

Short Interview Answer (30-60 seconds)

A delegate in C# is a type safe object that can reference one or more compatible methods. I can store a method in a delegate variable and invoke it later, which makes delegates useful for callbacks and event patterns. The compiler checks that the method is compatible with the delegate signature. Delegates are immutable, so combining or removing methods produces another delegate value rather than changing the existing delegate object.

Detailed Explanation

See the Code while reading this explanation.

A delegate lets a program remember an action and run that action later. Instead of deciding every action in one place, one part of the program can give another part a piece of work to perform. The receiver does not need to know the details of that work. It only needs to know what information must be provided and what result may come back. This makes programs easier to connect and change. It is commonly useful when one part needs to call another part after something happens.

Useful Questions to Ask the Interviewer
  1. Would you like me to explain both single method and multiple method delegates?
  2. Should I also compare delegates with events and lambda expressions?
What are delegates in C#? diagram
How to Explain It in an Interview

A delegate is a C# reference type that represents callable methods with a compatible parameter list and return type. For example, delegate int Operation(int a, int b); defines a delegate that can reference a compatible method that accepts two int values and returns an int.

A delegate instance contains information about the method to call. For an instance method, it also keeps a reference to the target object. For a static method, no target object is required. Calling the delegate invokes the referenced method.

The compiler provides type safety by checking delegate and method compatibility. Method groups and lambda expressions can be converted to compatible delegate types. Common built in delegate types include Action, Action<T>, Func<TResult>, and forms of Func with input parameters.

Delegates are immutable. Adding a method with += creates a combined delegate value containing an invocation list. Removing a method with -= also produces another delegate value. A multicast delegate normally invokes its methods in invocation list order. If an invocation throws and that exception escapes, later methods are not invoked. For a delegate with a return value, normal multicast invocation returns the value produced by the last method that successfully runs.

Delegates are also the basis of the C# event pattern. An event exposes controlled subscription and removal while normally preventing outside code from directly invoking the event.

In production code, consider lifetime and allocation behavior. A retained delegate can keep its target object reachable. A capturing lambda can also keep captured state reachable and may require compiler generated storage. Creating and combining delegates can allocate objects, so allocation sensitive code should avoid unnecessary delegate creation after measurement shows it matters.

Code
using System;

public static class Program
{
    // This delegate type defines the required parameter types and return type.
    public delegate int Operation(int a, int b);

    public static void Main()
    {
        // Add has a compatible signature, so the method group can become an Operation delegate.
        Operation operation = Add;

        // Invoking the delegate calls the method referenced by the delegate value.
        int result = operation(3, 4);

        // The Add method returns 7 for these inputs.
        Console.WriteLine(result);
    }

    // This method has the parameter types and return type required by Operation.
    private static int Add(int a, int b)
    {
        return a + b;
    }
}
Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how C# represents callable behavior in a type safe way. They also want to see whether the candidate understands method signature compatibility, callbacks, multicast invocation, event patterns, delegate lifetime, and the memory effects of retaining target objects or captured state.

Common interview mistakes

A common mistake is thinking a delegate stores only a method name. A delegate for an instance method also keeps a reference to its target object. Another mistake is assuming any method can be assigned to any delegate even when their signatures are incompatible. Candidates may also confuse delegates with events. A delegate value can normally be invoked by code that holds it, while an event controls how outside code subscribes and removes handlers and restricts outside invocation. Another mistake is assuming every method in a multicast delegate always runs. If one invocation throws and the exception escapes, later methods are not called. It is also incorrect to think += mutates the existing delegate object because delegates are immutable.

Interview tip

Start by saying that a delegate is a type safe callable reference represented by a C# reference type. Then show a small method assignment and invocation. Mention callbacks and events next. Finish with one practical detail such as delegate immutability, multicast exception behavior, or the fact that an instance method delegate can keep its target object reachable.

Interviewer may ask next
What happens if one method in a multicast delegate throws an exception?

Normal multicast invocation stops when that exception escapes. Methods that appear later in the invocation list are not automatically called. This matters when several independent handlers must each get a chance to run. Code can retrieve the invocation list and invoke each delegate separately with its own exception handling when that behavior is required. The tradeoff is greater control at the cost of more code and an explicit error handling policy.

What performance or memory costs can delegates introduce in production code?

Delegate creation and delegate combination can allocate objects, and invoking a delegate adds an indirect call compared with a direct method call. An instance method delegate also keeps its target object reachable, while a capturing lambda can keep captured state reachable and may require compiler generated storage. These costs usually matter only in allocation sensitive or frequently executed paths. Reusing delegate values and avoiding unnecessary captures can reduce pressure, but production optimization should be based on measurement rather than assumption.

43. What are Func, Action, and Predicate delegates?Language SpecificMedium

Question Details

Compare their return-value conventions, parameter shapes, and the common places each fits.

Short Interview Answer (30-60 seconds)

I use Func when the operation must return a value, Action when it returns nothing, and Predicate when one input is tested and the result is true or false. Func puts its return type last and can have zero through sixteen input parameters. Action can have zero through sixteen input parameters and always returns void. Predicate takes exactly one input and returns bool. They are standard .NET delegate types commonly used with lambdas, method groups, callbacks, collection APIs, and LINQ.

Detailed Explanation

See the Code while reading this explanation.

The practical difference is what the piece of work must produce. One form is used when some work receives values and gives a value back. Another is used when work performs an action but gives nothing back. The third is designed for a yes or no test on one value. Choosing the correct form makes the purpose of a callback easier to understand. It also lets library code accept behavior without requiring a new custom type for every small operation. The main decision is therefore whether the callback returns a value, returns nothing, or answers a condition.

Useful Questions to Ask the Interviewer
  1. Would you like an example using lambdas and method groups?
  2. Should I also compare Predicate with Func that returns bool?
What are Func, Action, and Predicate delegates? diagram
How to Explain It in an Interview

Func, Action, and Predicate are delegate types provided by .NET. A delegate is a type safe object that can refer to callable code such as a compatible method or lambda.

Func is used when the called code returns a value. Its last generic type argument is the return type. Any earlier generic arguments are input parameter types. Func<int> represents code that takes no input and returns an int. Func<int, int, int> represents code that takes two int values and returns an int. The standard Func family supports zero through sixteen input parameters.

Action is used when the called code returns void. Action represents code with no parameters. Action<string> represents code that accepts one string and returns nothing. The standard Action family supports zero through sixteen input parameters.

Predicate<T> represents code that accepts exactly one T value and returns bool. It is used by APIs that specifically ask whether an item matches a condition. For example, List<T>.Find and List<T>.RemoveAll accept Predicate<T>. LINQ methods commonly use Func<T, bool> instead. Enumerable.Where is a common example.

A compatible lambda or method group can be converted to the delegate type expected by the assignment or method call. However, Predicate<T> and Func<T, bool> remain different delegate types. An existing instance of one is not directly assignable to the other merely because their parameter and return shapes match.

Calling through a delegate has some invocation overhead compared with a direct call. Creating a delegate can also allocate an object. A lambda that captures local state can require a compiler generated closure object so that captured values remain available after the surrounding scope continues. These costs are usually small in ordinary code but can matter in frequently executed paths.

Code
using System;

public static class Program
{
    public static void Main()
    {
        // Func receives two integers and returns their calculated result.
        Func<int, int, int> add = (left, right) => left + right;

        // Action receives a string and performs work without returning a value.
        Action<string> print = message => Console.WriteLine(message);

        // Predicate receives one integer and answers a true or false condition.
        Predicate<int> isEven = value => value % 2 == 0;

        // Invoke each delegate using the same behavior described in the explanation.
        int sum = add(4, 6);
        print($"Sum: {sum}");
        print($"Is 10 even: {isEven(10)}");
    }
}
Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how C# represents callable behavior with delegate types and can choose the correct standard delegate for a callback. They also want to see whether the candidate knows the difference between returning a value, returning nothing, and testing one value to produce true or false. A strong answer also shows practical understanding of lambdas, method groups, delegate invocation, library APIs, and possible allocation costs from delegate creation and captured variables.

Common interview mistakes

A common mistake is saying that Func always takes one input. Func can have zero through sixteen input parameters, followed by one return type. Another mistake is saying that Action returns a value. Action always returns void. Predicate<T> takes exactly one T input and returns bool. Candidates also sometimes say that Predicate<T> and Func<T, bool> are the same delegate type. They can represent callable code with the same basic parameter and result shape, but they are distinct delegate types. Another mistake is assuming every lambda has the same allocation behavior. A lambda that captures surrounding local state can require a closure object, so unnecessary captures can increase memory use in frequently executed code.

Interview tip

Start with the return rule because it makes the three types easy to separate. Say Func returns a value, Action returns void, and Predicate tests one value and returns bool. Then give one short signature for each. If the interviewer asks for more depth, explain that Predicate<T> and Func<T, bool> are distinct delegate types and mention that captured lambdas can require additional allocations.

Interviewer may ask next
Can a Predicate<T> variable be assigned directly to a Func<T, bool> variable?

No. Predicate<T> and Func<T, bool> are distinct delegate types even though both can represent callable code that accepts T and returns bool. A compatible lambda or method group can be converted separately to either target delegate type, but an existing Predicate<T> instance is not directly assignable to a Func<T, bool> variable. This matters when connecting APIs because matching parameter and return shapes do not make two named delegate types identical.

What performance or memory costs can delegates and captured lambdas introduce in production code?

Delegate invocation has some call overhead, and creating a delegate can require an allocation. A lambda that captures local state can also require a compiler generated closure object that stores the captured values. That object may remain alive as long as the delegate that refers to it remains reachable. The tradeoff is usually acceptable because delegates provide clear and flexible callback behavior, but unnecessary delegate creation and captures can increase allocations and garbage collection work in frequently executed paths.

44. What are events in C#?Language SpecificHard

Question Details

Explain the publish/subscribe pattern in terms of delegates, event access, and how subscribers register and unregister.

Short Interview Answer (30-60 seconds)

In C#, an event is a controlled notification mechanism built on a delegate. A publisher exposes an event, subscribers register handler methods with the event, and the publisher raises the event when something happens. Subscribers normally use += to register and -= to unregister. Outside code can add or remove handlers, but it cannot normally invoke the event or replace its complete subscriber list. The declaring type controls when the notification is raised.

Detailed Explanation

See the Code while reading this explanation.

Events let one part of a program tell other parts that something happened without needing to know exactly who is listening. For example, an order can announce that it was completed. One part might then send a message. Another part might update a report. The order does not need to call each interested part directly. Other parts can choose to listen, and they can stop listening when they no longer need the notice. This keeps responsibilities separate and makes the program easier to change or extend.

Useful Questions to Ask the Interviewer
  1. Should I explain both custom delegate events and EventHandler based events?
  2. Should I cover subscriber lifetime, exceptions, and memory retention?
What are events in C#? diagram
How to Explain It in an Interview

A C# event is built on a delegate. The delegate defines the method signature that subscribers must match. A publisher can declare an event such as public event EventHandler<OrderCompletedEventArgs>? OrderCompleted;.

A subscriber registers a compatible handler with publisher.OrderCompleted += HandleOrderCompleted;. A multicast delegate can contain multiple registered handlers. When the publisher raises the event, the handlers in the current invocation list are called in their delegate invocation order. A subscriber removes its handler with publisher.OrderCompleted -= HandleOrderCompleted;.

The event keyword adds an important access restriction. Normal code outside the declaring type can add and remove handlers, but it cannot directly invoke the event or assign a new complete delegate value to it. The declaring type controls when the event is raised.

A common raising pattern is OrderCompleted?.Invoke(this, args). The null conditional operator safely handles the case where no handlers are registered. The receiver is evaluated once for that invocation. With concurrent subscription changes, an already captured invocation can still call a handler that was removed at nearly the same time, so unsubscription is not a guarantee that an overlapping invocation cannot reach that handler.

Normal event invocation is synchronous. A slow handler delays the publisher. If a handler throws an exception and that exception is not handled, invocation stops at that point and later handlers in the invocation list are not called.

Events are useful when one publisher can have zero or many interested listeners. They are less suitable when the publisher needs one specific collaborator or needs a result returned directly. A method call or interface is usually clearer for that case.

Subscriber lifetime also matters. A publisher stores delegate references to registered handlers. An instance handler contains a reference to its target object, so a reachable long lived publisher can keep a shorter lived subscriber reachable. Subscription and removal also create updated delegate values because delegates are immutable. Raising the event in this example allocates a new event data object each time Complete is called. These costs are usually small, but frequent events or large subscriber sets should be designed carefully.

Code
using System;

public sealed class OrderCompletedEventArgs : EventArgs
{
    public int OrderId { get; }

    public OrderCompletedEventArgs(int orderId)
    {
        // Store the data that subscribers need when the notification is raised.
        OrderId = orderId;
    }
}

public sealed class Order
{
    // Expose controlled subscription while Order keeps control of event invocation.
    public event EventHandler<OrderCompletedEventArgs>? OrderCompleted;

    public void Complete(int orderId)
    {
        Console.WriteLine($"Completing order {orderId}");

        // Create event data for this completion and notify the current subscribers.
        OnOrderCompleted(new OrderCompletedEventArgs(orderId));
    }

    private void OnOrderCompleted(OrderCompletedEventArgs args)
    {
        // Invoke the current handler list if at least one subscriber exists.
        OrderCompleted?.Invoke(this, args);
    }
}

public sealed class NotificationService
{
    public void HandleOrderCompleted(object? sender, OrderCompletedEventArgs args)
    {
        // React to the notification without requiring Order to know this service directly.
        Console.WriteLine($"Notification received for order {args.OrderId}");
    }
}

public static class Program
{
    public static void Main()
    {
        var order = new Order();
        var notifications = new NotificationService();

        // Register this handler for future OrderCompleted notifications.
        order.OrderCompleted += notifications.HandleOrderCompleted;

        order.Complete(42);

        // Remove the same handler when this subscriber no longer needs notifications.
        order.OrderCompleted -= notifications.HandleOrderCompleted;

        // No notification handler runs because the subscriber was removed before this call.
        order.Complete(43);
    }
}
Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how C# implements notifications between objects. They want to see knowledge of delegates, event access rules, subscriber registration and removal, invocation behavior, object lifetime, exception behavior, and the practical risks of keeping subscribers registered for too long.

Common interview mistakes

A common mistake is treating an event like an ordinary public delegate field. The event keyword prevents normal outside code from invoking the event or replacing its complete subscriber list. Another mistake is forgetting to remove a shorter lived subscriber from a reachable long lived publisher, which can keep the subscriber reachable through the delegate target reference. Candidates also sometimes assume handlers run asynchronously. Normal event invocation is synchronous. Another mistake is assuming that removing a handler guarantees that no overlapping invocation can still call it. Concurrent invocation may already have captured the handler. Finally, an unhandled exception from one handler can prevent later handlers from running.

Interview tip

Start by saying that an event is a controlled delegate based notification mechanism. Then explain the three important actions: the publisher declares and raises the event, subscribers register with +=, and subscribers remove handlers with -=. Finish with the access restriction, synchronous invocation behavior, exception effect, and subscriber lifetime concern.

Interviewer may ask next
Can a subscriber still be called if it is removed while the event is being raised?

Yes, an overlapping invocation can still call that subscriber if the invocation has already captured a delegate value that contains the handler. Removing the handler changes the event's delegate value for later access, but it does not rewrite a delegate value that an invocation already obtained. This matters in concurrent code because unsubscription alone is not a synchronization guarantee. A subscriber that requires strict shutdown behavior needs separate coordination for work that may already be in progress.

What are the performance and production tradeoffs of an event with many subscribers?

Raising a normal C# event calls its registered handlers synchronously, so total work grows with the number and cost of handlers. Subscription and removal create updated delegate values because delegates are immutable, and this example also allocates an OrderCompletedEventArgs object for each raise. A slow handler delays the publisher, and an unhandled exception can stop later handlers from running. Events are a good fit for simple in process notifications, but high frequency or expensive work may require a design that deliberately handles scheduling, failures, allocation pressure, and subscriber lifetime.

45. What are attributes in C#?Language SpecificEasy

Question Details

Explain how attributes annotate code, where they are commonly applied, and what kinds of metadata-driven behavior they enable at compile time or runtime.

Short Interview Answer (30-60 seconds)

Attributes are metadata tags in C# that I place in square brackets. They attach extra facts to a type, member, or parameter, and tools or frameworks can read those facts later. I use them for things like marking code as obsolete, guiding serialization, or helping reflection based code make decisions.

Detailed Explanation

Attributes are small notes I attach to code. They do not change the main logic by themselves. They tell tools, the compiler, or the runtime extra facts about a class, method, property, or parameter. That makes code easier to guide without adding special if statements everywhere. For example, one attribute can warn that a method should not be used, while another can help a serializer rename or skip a property. In an interview, I would say they are a clean way to attach metadata to code.

Useful Questions to Ask the Interviewer
  1. Do you want me to focus on built in attributes or custom attributes?
  2. Should I explain how reflection reads attributes at runtime?
What are attributes in C#? diagram
How to Explain It in an Interview

In C#, attributes are metadata. You write them in square brackets above a declaration, such as a class, method, property, or parameter. Most attributes do not hold the real business logic. Instead, they store extra information in the assembly so the compiler, the runtime, or another library can read it later.

A simple example is Obsolete. If I mark a method with that attribute, the compiler can warn other developers that the method should not be used. Another common use is serialization. A JSON library can read attributes to decide how to name a property or whether to ignore it. Test frameworks also use attributes to find test methods.

The main value is that the code stays clean while still carrying useful instructions. That is why attributes fit well with frameworks and tools that work by reading metadata. Some attributes affect compile time warnings. Others matter only when a framework or reflection code reads them at runtime.

There are also limits. An attribute is not the same as the behavior itself. If no tool or library reads it, it does nothing useful. Reflection can also add some overhead if code checks attributes often. In production, I use attributes when I want a clear declarative hint. I do not use them for core business rules when plain code is easier to follow.

Why Interviewers Ask This

They want to see whether I know how C# attaches metadata to code, how the compiler and frameworks can read it, and when attributes are better than hard coded logic.

Common interview mistakes

A common mistake is thinking attributes are the same as program logic. They are not. Another mistake is assuming every attribute changes runtime behavior by itself. Many attributes only matter if a compiler, framework, or reflection code reads them. People also forget that custom attributes must inherit from System.Attribute. Another mistake is using attributes for core business rules when plain code would be clearer.

Interview tip

Start with the simple idea that attributes are metadata in square brackets, then give one built in example like Obsolete or serialization.

Interviewer may ask next
Can I create my own attribute in C#?

Yes. You can create a custom attribute by making a class that inherits from System.Attribute. You apply it with square brackets just like a built in attribute. This matters when your own app or library needs extra metadata that frameworks or reflection code can read later.

Do attributes affect performance?

Usually the attribute itself has little direct runtime cost, but reading it with reflection can add overhead. That matters in production if you inspect attributes many times or on a hot path. The tradeoff is that attributes give clean declarative metadata, but heavy reflection use can be slower than direct code.

46. What are anonymous types?Language SpecificMedium

Question Details

Focus on compiler-generated read-only shape, local projection use, and why they are usually limited to a method scope.

Short Interview Answer (30-60 seconds)

Anonymous types let me create a small temporary object without declaring a named class first. C# infers the property names and types, and the compiler creates a reference type with read only properties. I mainly use them for local projections, especially with LINQ. They are usually kept inside a method because the generated type name cannot be written directly in normal C# source code, so it is not suitable as a normal method signature or public contract.

Detailed Explanation

Anonymous types are useful when I need to group a few values for a short local task but do not need to create a permanent named class. I can create an object with the values I need, and C# works out the names and types of its properties. I can then read those values by property name. This is common when a query needs only part of a larger object, such as a customer name and city. The shape is normally kept inside one method because its generated type does not have a name that normal C# source code can directly use.

Useful Questions to Ask the Interviewer
  1. Do you want me to focus on anonymous types in LINQ projections?
  2. Should I compare anonymous types with named classes, records, or tuples?
What are anonymous types? diagram
How to Explain It in an Interview

An anonymous type is a compiler generated reference type created from an anonymous object creation expression such as new { Name = "Maya", Age = 30 }. I do not declare a class for this shape. The compiler infers each property name and type from the expressions I provide. A local variable normally uses var so the compiler can preserve the exact generated type.

The generated type is a sealed reference type. Its properties are public and read only. I can read person.Name, but I cannot later assign another value to that property. The compiler generates the constructor and the property accessors needed to hold those initial values.

A common use is a LINQ projection. For example, a query can select only Name and City from a larger customer object. This creates a small shape that is useful for the remaining local query work without requiring a separate named class.

Anonymous types are normally kept local because their generated type names cannot be referenced directly in normal C# source code. That prevents using the exact anonymous type as an ordinary declared method return type, parameter type, field type, or public contract. The object can technically be treated as object or dynamic, but doing that either loses direct static access to its anonymous properties or gives up compile time member checking, so it does not remove the main design limitation.

The compiler also generates value based Equals and GetHashCode behavior. Instances of the same anonymous type compare their corresponding property values. Property names, compile time property types, and property order determine whether anonymous object initializers in the same program produce the same anonymous type.

Because anonymous types are reference types, creating an instance normally allocates an object that is managed by the garbage collector. This cost is usually acceptable for local projections, but large numbers of temporary objects can create allocation pressure. For reusable domain data, shared contracts, method boundaries, or public APIs, I would normally choose an explicit named type instead.

Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands temporary object shapes in C#, compiler generated types, type inference, read only properties, value based equality, and common LINQ projection use. They also want to know whether the candidate understands why anonymous types work well for local data shaping but are usually a poor choice for reusable contracts or public APIs.

Common interview mistakes

A common mistake is treating an anonymous type as the same thing as dynamic. It is not. With var, the compiler knows the exact generated type and checks property access at compile time. Another mistake is expecting its properties to be writable after creation. They are read only. Candidates also sometimes assume that an anonymous type is a value type, but it is a reference type and creating an instance normally requires object allocation. Another mistake is trying to make the anonymous type itself a normal method return type, parameter type, field type, or public contract even though its generated name cannot be referenced directly in normal C# source code.

Interview tip

Start by saying that an anonymous type is a compiler generated reference type for a temporary data shape. Then mention inferred property names and types, read only properties, var, and LINQ projections. Finish with the key limitation that its generated type name cannot be written directly in normal C# source code, so anonymous types are best kept in local implementation logic.

Interviewer may ask next
When do two anonymous objects have the same anonymous type and compare as equal?

They have the same anonymous type when their anonymous object initializers in the same program have properties with the same names and compile time types in the same order. Instances of that same anonymous type use compiler generated value based Equals and GetHashCode behavior, so Equals returns true when all corresponding property values are equal according to those property types. This matters for operations such as grouping and distinct filtering because equality is based on the contained values rather than only on reference identity.

When should you use a named type instead of an anonymous type?

Use a named type when the data shape needs to be reused, cross a clear method or layer boundary, represent a stable domain concept, appear in a public API, or serve as a durable contract. Anonymous types are better for temporary local projections because their generated type names cannot be referenced directly in normal C# source code. A named class or record adds an explicit reusable contract, while an anonymous type keeps small local transformations concise. Anonymous types are reference types, so code that creates very large numbers of temporary instances should also consider the allocation and garbage collection cost.

47. What is reflection in C#?Language SpecificHard

Question Details

Explain how reflection inspects types and members at runtime, and mention why it is powerful but more expensive than direct code paths.

Short Interview Answer (30-60 seconds)

Reflection lets C# code inspect types, properties, methods, fields, constructors, and attributes while the program is running. I would use APIs such as Type, PropertyInfo, and MethodInfo when the exact type or member must be discovered at runtime. It is powerful for framework and infrastructure code, but lookup and reflective invocation generally cost more than direct strongly typed access, so I avoid repeated reflection on hot execution paths when a direct approach is practical.

Detailed Explanation

See the Code while reading this explanation.

Reflection lets a running program examine information about the code it is working with. For example, the program can discover what properties an object has, what actions its type provides, or what extra labels were placed on its code. It can make decisions using information that was not directly written into the normal path of the program. This is useful when building reusable tools that must work with many different types. The tradeoff is that discovering and using this information while the program runs takes more work than calling known code directly.

Useful Questions to Ask the Interviewer
  1. Should I explain both inspecting members and invoking them with reflection?
  2. Would you like a production example involving attributes or properties?
What is reflection in C#? diagram
How to Explain It in an Interview

Reflection in .NET is the ability to inspect metadata about assemblies, types, and their members at runtime. System.Type is central to reflection. From a Type object, code can discover constructors, methods, properties, fields, events, interfaces, generic information, and attributes. Objects such as PropertyInfo and MethodInfo describe individual members. ([learn.microsoft.com](https://learn.microsoft.com/en-us/dotnet/fundamentals/reflection/overview?utm_source=chatgpt.com))

A program can obtain a Type from a known type with typeof or from an object with GetType. It can then use methods such as GetProperties or GetMethod to discover members. Reflection can also read attributes, get or set property values, create objects, and invoke methods dynamically.

This is useful when reusable code cannot know every application type in advance. Typical examples include serializers, dependency injection infrastructure, plugin discovery, object mapping, test tools, and code that reads custom attributes.

The tradeoff is runtime cost and weaker compile time checking. Direct code already identifies the member being accessed. Reflection adds metadata lookup and runtime validation. Reflective invocation can also require objects such as argument arrays and can cause boxing when value type arguments are represented as object values. A wrong member name, incompatible argument, or missing member may therefore fail only when that path runs.

For production code, I use reflection when runtime discovery provides real value. If the same member metadata is used repeatedly, I can reuse the discovered information rather than repeat the lookup. If invocation is performance sensitive, a cached delegate, generated code, or direct strongly typed code may be more appropriate. Reflection also needs extra care with trimming and Native AOT when required members cannot be determined statically. Long lived caches should also be designed carefully when types come from collectible AssemblyLoadContext instances because retained type or member metadata can prevent those loaded components from being collected.

Code
using System;
using System.Reflection;

public sealed class Person
{
    public string Name { get; set; } = "Asha";

    public void Greet()
    {
        Console.WriteLine($"Hello, {Name}");
    }
}

public static class Program
{
    public static void Main()
    {
        var person = new Person();

        // Get the runtime type so the program can inspect metadata about this object.
        Type type = person.GetType();

        // Find the public Name property by metadata rather than accessing it directly.
        PropertyInfo? nameProperty = type.GetProperty("Name");

        if (nameProperty is not null)
        {
            // Read the property only after confirming that the runtime lookup succeeded.
            object? value = nameProperty.GetValue(person);
            Console.WriteLine($"Name: {value}");
        }

        // Find the public instance method at runtime by its metadata name.
        MethodInfo? greetMethod = type.GetMethod("Greet");

        if (greetMethod is not null)
        {
            // Invoke the discovered method on this object. The method takes no arguments.
            greetMethod.Invoke(person, null);
        }
    }
}
Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how .NET can inspect type metadata while a program is running. They also want to see whether the candidate knows when reflection is useful, what errors can move from compile time to runtime, and why repeated reflective lookup or invocation should be treated carefully on performance sensitive production paths.

Common interview mistakes

A common mistake is treating reflection as if it has the same behavior and checking as normal direct member access. Reflection performs runtime discovery, so requested members may not exist and errors can appear only when the code executes. Another mistake is using GetProperty or GetMethod without checking for null. Developers can also forget that visibility, inheritance, overloads, parameter types, and BindingFlags can change which member is found. Repeating the same metadata lookup inside a hot loop can add unnecessary work. Reflective invocation can also add allocations or boxing depending on the arguments and API used. Another production mistake is assuming every reflection pattern is automatically safe with trimming or Native AOT. Finally, caches that permanently retain Type or MemberInfo objects can be a problem when the application expects a collectible AssemblyLoadContext to unload.

Interview tip

Start by saying that reflection inspects type metadata at runtime. Then name Type, PropertyInfo, and MethodInfo as examples. Give one practical use such as reading attributes or discovering properties. Finish with the main tradeoff: reflection provides runtime flexibility, but direct strongly typed code is normally simpler, safer at compile time, and cheaper to execute.

Interviewer may ask next
What happens if reflection cannot find the property or method you request?

The lookup can return null when no matching member is found. For example, Type.GetProperty and Type.GetMethod have overloads that return null when a suitable member is unavailable. This matters because the compiler cannot guarantee that a member requested dynamically by name exists. Visibility, overloads, parameter types, inheritance, and BindingFlags can also affect the result. Production code should therefore use precise lookup criteria when needed and validate the returned metadata before trying to use it.

How would you reduce reflection cost when the same member is used repeatedly?

I would avoid repeating the metadata discovery and reuse the discovered member information when its lifetime is appropriate. If repeated invocation is still performance sensitive, I can create and cache a compatible delegate or use generated or direct strongly typed code instead of repeatedly calling MethodInfo.Invoke. Delegate invocation can reduce the overhead of repeated reflective invocation. ([learn.microsoft.com](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-delegate-createdelegate?utm_source=chatgpt.com)) The tradeoff is additional implementation and cache management complexity. I would also avoid a global cache that keeps collectible plugin types alive when AssemblyLoadContext unloading is required.

48. What is the difference between IEnumerable and IQueryable in C#?Language SpecificMedium

Question Details

Explain how it works, the main tradeoffs, the most common pitfalls, and one practical situation where it is the better choice.

Short Interview Answer (30-60 seconds)

IEnumerable is mainly used when the data is already in the application, while IQueryable is used when a query provider can inspect the query and decide how to execute it. With IEnumerable, LINQ operations normally use delegates and run as the sequence is enumerated. With IQueryable, LINQ operations build an expression tree that a provider can translate. For example, Entity Framework Core can translate supported filtering and projection into a database query. I use IQueryable while I still want the provider to do that work, then materialize the results before application side processing.

Detailed Explanation

See the Code while reading this explanation.

The simple difference is where the work happens. IEnumerable is useful when the application already has the data it needs. You can then read that data and apply operations such as filtering or selecting items. IQueryable is useful when another system can understand the requested work before returning the data. This can allow only the needed data to be returned instead of bringing everything into the application first. Both can wait until you actually read the results before doing the work.

Useful Questions to Ask the Interviewer
  1. Is the data already in memory, or does a query provider supply it?
  2. Should filtering and projection happen before the results are brought into the application?
What is the difference between IEnumerable and IQueryable in C#? diagram
How to Explain It in an Interview

IEnumerable<T> represents a sequence that can be enumerated. LINQ operations on an IEnumerable<T> normally use delegates and execute over the sequence as it is enumerated. IQueryable<T> also represents an enumerable query, but its LINQ operations can build expression trees. A query provider can inspect those expressions and translate supported operations into its own query language. Entity Framework Core is a common example when querying a database.

The practical benefit of IQueryable is that filtering, sorting, and projection can stay with the provider before materialization. This can reduce the rows, columns, data transfer, and application memory required. However, IQueryable does not automatically mean database execution or better performance. It depends on the provider and what that provider can translate.

Both forms can use deferred execution. Writing the query does not normally execute it immediately. Enumeration or a terminal operation causes execution.

A useful production pattern is to keep provider supported filtering, sorting, and projection on IQueryable until the query has the shape you need. Then materialize the results and use IEnumerable for application side processing. A common mistake is calling ToList too early, because operations after that point run against the materialized data. Another mistake is assuming every C# method can be translated by an IQueryable provider. Unsupported expressions may fail during translation or require a different query shape.

Code
using System;
using System.Collections.Generic;
using System.Linq;

public static class Program
{
    public static void Main()
    {
        var numbers = new[] { 1, 2, 3, 4, 5 };

        // IEnumerable uses normal LINQ delegates over the in memory sequence.
        // The filtering work is performed when the result is enumerated.
        IEnumerable<int> enumerable = numbers;
        var enumerableResult = enumerable.Where(number => number > 2);

        // AsQueryable exposes the in memory source through IQueryable.
        // The Where call builds an expression based query for this provider.
        IQueryable<int> queryable = numbers.AsQueryable();
        var queryableResult = queryable.Where(number => number > 2);

        // Enumerating both results applies the deferred queries and prints the values.
        Console.WriteLine(string.Join(", ", enumerableResult));
        Console.WriteLine(string.Join(", ", queryableResult));
    }
}
Why Interviewers Ask This

This question checks whether you understand where LINQ query work happens. It tests the difference between processing data through normal C# enumeration and building a query that a provider can inspect and translate. The interviewer is also checking practical judgment around deferred execution, query translation, data transfer, application memory use, provider limitations, and when to materialize results.

Common interview mistakes

A common mistake is saying that IQueryable always means a database query. It does not. IQueryable depends on its query provider, and AsQueryable on an in memory collection still uses an in memory provider. Another mistake is assuming IQueryable is always faster. The benefit depends on provider translation, query shape, data size, indexes, and the amount of data returned. Developers can also call ToList too early, which materializes the current query and causes later LINQ operations to run in memory. Another mistake is assuming every C# method can be translated by every IQueryable provider.

Interview tip

Start with the practical difference: IEnumerable normally processes an existing sequence, while IQueryable builds a provider inspectable query. Then explain that both can use deferred execution. Give the Entity Framework Core example and mention that early materialization moves later work into application memory. This shows both C# understanding and production judgment.

Interviewer may ask next
What happens if you call ToList before applying a Where filter?

The Where filter runs in memory after ToList materializes the current query. For a database backed IQueryable, any filter added after ToList is no longer part of the database query. This can cause more rows to be transferred and stored in application memory than necessary. The important behavior is that ToList ends the provider based query stage for the returned collection, so later LINQ operations run against that materialized data.

Why can IQueryable improve performance for a database query?

IQueryable can improve performance when the provider translates supported filtering, sorting, or projection into the database query before materialization. The database can then return only the rows and columns that the application needs, which can reduce data transfer and application memory use. The tradeoff is that translation depends on provider support and query shape, so IQueryable is not a guarantee of better performance.

49. What is LINQ?Language SpecificMedium

Question Details

Describe how query operators work over sequences and why deferred execution matters for correctness and performance.

Short Interview Answer (30-60 seconds)

LINQ is a set of C# and .NET query features that let me filter, transform, sort, group, and combine data in a consistent way. With LINQ over IEnumerable<T>, many operators such as Where and Select use deferred execution. They describe the work first and usually perform it when the sequence is enumerated. This matters because the source can change before enumeration, and enumerating the same query again can repeat the work.

Detailed Explanation

See the Code while reading this explanation.

LINQ gives a program a simple way to ask for selected information from a group of values. For example, a program can keep only active customers, choose their names, and sort those names. An important detail is that describing these steps does not always perform the work immediately. In many cases, the work happens later when the program actually reads the results. This can avoid work that is never needed, but it also means that changes made to the original values before they are read can affect the final result.

Useful Questions to Ask the Interviewer
  1. Should I focus on LINQ over in memory sequences using IEnumerable<T>?
  2. Would you like an example of deferred execution and repeated enumeration?
What is LINQ? diagram
How to Explain It in an Interview

LINQ means Language Integrated Query. It provides standard query operators such as Where, Select, OrderBy, GroupBy, Any, First, and ToList. These operators let C# code query sequences using a consistent style.

For IEnumerable<T>, operators such as Where and Select normally use deferred execution. Calling them creates an object that represents how values should be produced. It does not normally enumerate the source at that moment. Enumeration starts when code requests values, such as through foreach, or when an operator such as ToList consumes the sequence to produce a result immediately.

For example, suppose a List<int> contains 1, 2, and 3. I create a Where query that keeps values greater than 1. I then add 4 to the source list before enumerating the query. When the query is enumerated, it can produce 2, 3, and 4 because it reads the source at enumeration time.

If I instead call ToList before a later source change, ToList enumerates the query immediately and creates a separate List<int> containing the produced values. Later changes to the original list do not automatically change that new list.

Operator behavior also matters. Where and Select can usually process values as they are requested. An operator such as OrderBy must read and buffer its input before it can produce values in sorted order. Repeatedly enumerating a deferred query can also repeat its work.

Deferred execution is useful when results may never be needed, when early filtering can reduce later work, or when the current source state should be observed. In production code, I materialize with ToList or ToArray when I need a stable snapshot or intentionally want the query to execute once.

Code
using System;
using System.Collections.Generic;
using System.Linq;

public static class Program
{
    public static void Main()
    {
        var numbers = new List<int> { 1, 2, 3 };

        // Build a deferred query. Where stores the filtering logic without enumerating the source
        // here.
        IEnumerable<int> deferredQuery = numbers.Where(number => number > 1);

        // Change the source before enumeration so the deferred query can observe the current source
        // contents.
        numbers.Add(4);

        // Enumerate the query now and store the produced values in a separate list.
        List<int> firstSnapshot = deferredQuery.ToList();
        Console.WriteLine(string.Join(", ", firstSnapshot));

        // Materialize another query now because this result should remain a stable snapshot.
        List<int> stableSnapshot = numbers.Where(number => number > 1).ToList();

        // Change only the original source. The already created snapshot remains unchanged.
        numbers.Add(5);
        Console.WriteLine(string.Join(", ", stableSnapshot));
    }
}
Why Interviewers Ask This

Interviewers ask this to check whether a candidate understands how C# queries sequences and whether they understand when query work actually happens. They also want to see practical judgment around deferred execution, repeated enumeration, operator ordering, buffering, materialization, and the effect of source changes on query results.

Common interview mistakes

A common mistake is assuming every LINQ operator executes when the query expression is created. Many operators over IEnumerable<T> are deferred. Another mistake is enumerating the same deferred query several times without realizing that its work can run again each time. Developers may also assume a deferred result is a snapshot even though changes to the source before later enumeration can affect the values produced. Another mistake is assuming every operator streams values. OrderBy must consume and buffer its input before returning correctly ordered values. Calling ToList unnecessarily can also add execution and memory cost when a snapshot is not needed.

Interview tip

Start by defining LINQ as a consistent way to query sequences. Then explain deferred execution with one simple source change example. Make it clear that Where and Select usually describe work, enumeration performs that work, and ToList forces enumeration and stores the produced values. Mention repeated enumeration and buffering operators to show practical production understanding.

Interviewer may ask next
What happens if the source collection changes after a deferred LINQ query is created but before it is enumerated?

The deferred query normally reads the source during enumeration, so a change made before enumeration can affect the values produced. For example, if a matching value is added to a List<T> after Where is called but before the query is enumerated, that value can appear in the results. This matters because creating the query does not create a snapshot. If code needs a stable result, calling ToList or ToArray at the intended point in time enumerates the query and stores the produced values separately.

When should you materialize a LINQ query with ToList instead of leaving it deferred?

I use ToList when I need a stable snapshot, when I intentionally want the query to execute once, or when repeating an expensive enumeration would be undesirable. ToList enumerates the source immediately and allocates a new List<T> containing the produced values. The tradeoff is that this uses additional memory and performs the work at that moment. Leaving the query deferred can avoid unnecessary execution and allocation, but later enumeration can observe changed source data and repeated enumeration can repeat the query work.

50. What is the difference between query syntax and method syntax in LINQ?Language SpecificMedium

Question Details

Compare the two expression styles, where each is more readable, and how they map to the same underlying operators.

Short Interview Answer (30-60 seconds)

They are two ways to write the same LINQ query. Query syntax often reads more like a data request, while method syntax is the direct chain of LINQ methods and works for every operator. In C#, query syntax is usually translated by the compiler into method calls such as Where, Select, GroupBy, and Join.

Detailed Explanation

This question asks about two ways to write the same LINQ query in C#. One style looks like a small request written inside the language. The other style uses normal method calls with dots. Both can get the same result from the same data. The interviewer wants to know whether I can read both forms and choose the one that is easier to understand in real code. It also checks that I know they are not two different features with different results.

Useful Questions to Ask the Interviewer
  1. Does your team prefer one LINQ style?
  2. Are joins and groupings common in this codebase?
What is the difference between query syntax and method syntax in LINQ? diagram
How to Explain It in an Interview

Query syntax is shorthand for method syntax. The C# compiler lowers query keywords into method calls when a matching translation exists. For example, from, where, select, join, group, and orderby map to methods such as Select, Where, Join, GroupBy, and OrderBy. That is why both styles usually produce the same runtime result and similar performance.

The main difference is readability. Query syntax often feels easier when the query looks like SQL, especially with joins and groupings. Method syntax often feels easier when you have a long chain of transformations, lambda expressions, or a LINQ operator that has no query keyword. Not every LINQ operator has a query form, so method syntax is the more complete and flexible style. In production, the best choice is usually the style that makes the query easiest for your team to read and maintain. The important point is that the choice is mostly about clarity, not about different results.

Why Interviewers Ask This

Interviewers ask this to check that you know LINQ is one feature with two forms, that you understand compiler translation, and that you can choose the clearest style in real code.

Common interview mistakes

A common mistake is thinking query syntax is faster. It is usually just a different way to write the same query. Another mistake is assuming every LINQ operator has a query form. Some operators only exist as methods. A third mistake is forgetting that deferred execution still applies in both styles when the sequence supports it.

Interview tip

Say that query syntax is shorthand and method syntax is the full direct form. Then add that you choose the style that is easiest to read for that query.

Interviewer may ask next
What happens when a LINQ operator has no query form?

Query syntax does not cover every LINQ operator. When there is no query keyword form, C# uses method syntax directly. This matters because some useful operators only exist as methods, so you should know both styles and switch to method syntax when needed.

Which style should a team standardize on?

Use the style that makes the code easiest to read for the team. Query syntax is often clearer for joins and groupings, while method syntax is often clearer for long transformation chains. The tradeoff is readability and consistency, not behavior, because both styles produce the same LINQ result when they map to the same operators.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.