Priprema za C# backend intervju

C# pitanja i odgovori za intervju 2026

Fokusirana kolekcija C# pitanja za backend intervjue. Koristite svaki odgovor kao strukturu za usmenu vežbu: koncept, primer i trade-offs.

Započni C# mock intervjuKreditna kartica nije potrebna. Dostupna je 1 besplatna sesija.
Vežba tehničkog intervjua na engleskomRežim u kome kandidati kojima engleski nije maternji mogu da vežbaju tehničke intervjue.

Types and Language Basics

1Kako se vrednosni tipovi razlikuju od referentnih tipova?

Promenljive vrednosnog tipa obično sadrže samu vrednost, pa se pri dodeli vrednost kopira. Promenljive referentnog tipa sadrže referencu na objekat, pa dodela kopira samo referencu i obe promenljive mogu videti promene na istom objektu. Važna nijansa je to što mesto skladištenja zavisi od životnog veka i konteksta, a ne samo od tipa: vrednost lokalna za metodu može se nalaziti na steku, dok vrednosni tip skladišten kao polje klase, element niza, vrednost uhvaćena u zatvorenju (closure capture), omotana vrednost ili polje asinhronog automata stanja živi zajedno sa tim objektom.

int a = 10;
int b = a;
b = 20;

// a is still 10

var first = new User { Name = "Ann" };
var second = first;
second.Name = "Kate";

// first.Name is also "Kate"
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

2Kako se razlikuju class, struct i record?

`class` je referentni tip sa identitetom objekta. `struct` je vrednosni tip i najbolji je za male nepromenljive vrednosti kao što su koordinate, opsezi ili identifikatori. `record class` je referentni tip sa jednakošću zasnovanom na vrednosti; `record struct` je verzija vrednosnog tipa. Zapisi (`record`) predstavljaju sintaksu kompajlera preko običnih CLR (Common Language Runtime) tipova koja generiše logiku jednakosti, `GetHashCode`, `ToString`, dekonstrukciju i podršku za kopiranje, uključujući `with` izraze za nedestruktivnu mutaciju. U backend kodu, zapisi su korisni za modele nalik DTO (Data Transfer Object) strukturama, dok su velike promenljive strukture rizične jer kopiranje i modifikovanje kopija može dovesti do neočekivanih rezultata.

public record User(int Id, string Name);

var first = new User(1, "Alice");
var second = new User(1, "Alice");

Console.WriteLine(first == second); // true
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

3Koja je razlika između `ref`, `out` i `in` parametara?

`ref` prosleđuje argument po referenci i zahteva da on bude inicijalizovan pre poziva; metoda može da ga čita i menja. `out` takođe prosleđuje po referenci, ali metoda mora da mu dodeli vrednost pre nego što se završi. `in` prosleđuje referencu samo za čitanje (readonly byref), što može sprečiti kopiranje velikih struktura, ali ne garantuje duboku nepromenljivost: promenljive strukture i članovi koji nisu označeni kao `readonly` mogu izazvati defanzivno kopiranje. Na nivou IL (Intermediate Language) koda, `ref`, `out` i `in` su upravljani parametri po referenci sa različitim pravilima u jeziku C#.

void Increment(ref int value) => value++;

bool TryRead(string text, out int value) =>
    int.TryParse(text, out value);

double Length(in Vector vector) =>
    Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

4Šta su omotavanje (boxing) i odmotavanje (unboxing)?

Omotavanje smešta vrednosni tip u objekat referentnog tipa tako što alocira objekat i kopira vrednost u njega. Odmotavanje izdvaja vrednost nazad i zahteva tačno kompatibilan tip. Često omotavanje povećava broj alokacija i opterećenje na GC (Garbage Collector). Generici izbegavaju mnoge slučajeve omotavanja jer `List<int>` čuva `int` vrednosti bez konvertovanja svake vrednosti u `object`.

int number = 42;
object boxed = number;      // boxing
int restored = (int)boxed;  // unboxing

object value = 42;
// long wrong = (long)value; // InvalidCastException
long ok = (long)(int)value;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

5Šta su nullable tipovi vrednosti i nullable referentni tipovi?

Nullable tipovi vrednosti koriste `Nullable<T>` ili `T?` i predstavljaju stvarni omotač u izvršnom okruženju za tipove vrednosti. Nullable referentni tipovi su prvenstveno analiza kompajlera: `string?` saopštava kompajleru da referenca može biti `null`, ali ne stvara novi tip tokom izvršavanja. Operator `!` (null-forgiving operator) samo potiskuje upozorenja; on ne štiti od `NullReferenceException` izuzetka.

int? age = null;

string name = "Alice";
string? optionalName = null;

var length = optionalName?.Length;
var actualName = optionalName ?? "Unknown";
optionalName ??= "Default";

string trusted = optionalName!;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

6Kako se razlikuju const, readonly i static readonly?

const je konstanta u vreme kompajliranja ograničena na primitivne tipove, enum vrednosti, string ili null, i njena vrednost se tokom kompajliranja direktno umeće u sklopove koji je koriste. readonly je polje instance koje se može dodeliti u deklaraciji ili konstruktoru. static readonly je jedno polje po tipu, koje se dodeljuje u deklaraciji ili statičkom konstruktoru. Javne vrednosti koje se mogu menjati kroz verzije biblioteke bezbednije su kao static readonly nego kao javni const, jer klijenti mogu zadržati staru ugrađenu const vrednost sve dok se ponovo ne kompajliraju.

public const int MaxAttempts = 3;

private readonly Guid _id = Guid.NewGuid();

public static readonly TimeSpan Timeout =
    TimeSpan.FromSeconds(30);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

7Kako Equals, == i GetHashCode funkcionišu zajedno?

Za obične klase, Equals i == podrazumevano porede reference. Mnogi ugrađeni tipovi i `record` tipovi porede se po vrednosti. Ako je Equals predefinisan (overridden), GetHashCode mora biti dosledan: ako je a.Equals(b) tačno (`true`), oba objekta moraju imati isti heš kod. Obrnuto nije zagarantovano. Nikada nemojte menjati polja koja se koriste za poređenje jednakosti ili heširanje dok je objekat ključ u Dictionary ili element u HashSet.

public sealed class User : IEquatable<User>
{
    public required int Id { get; init; }

    public bool Equals(User? other) =>
        other is not null && Id == other.Id;

    public override bool Equals(object? obj) =>
        obj is User other && Equals(other);

    public override int GetHashCode() =>
        Id.GetHashCode();
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

8Kako se override razlikuje od new?

override menja virtuelno ponašanje, pa pozvana metoda zavisi od tipa objekta u vreme izvršavanja. new sakriva člana iz osnovne klase, a izabrani član zavisi od statičkog tipa promenljive. Za polimorfizam se obično koriste virtual i override; sakrivanje pomoću new je ređe i može učiniti ponašanje zbunjujućim.

class Base
{
    public virtual void First() => Console.WriteLine("Base.First");
    public void Second() => Console.WriteLine("Base.Second");
}

class Derived : Base
{
    public override void First() => Console.WriteLine("Derived.First");
    public new void Second() => Console.WriteLine("Derived.Second");
}

Base value = new Derived();
value.First();  // Derived.First
value.Second(); // Base.Second
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

9Kako se apstraktna klasa razlikuje od interfejsa?

Apstraktna klasa može sadržati stanje, konstruktore, polja, implementirane metode, apstraktne metode i zaštićene članove. Interfejs opisuje ugovor; moderni interfejsi mogu uključivati podrazumevane implementacije i statičke apstraktne članove, ali i dalje nisu uobičajeno mesto za stanje instance. Klasa može naslediti jednu osnovnu klasu, ali može implementirati više interfejsa. Koristite apstraktnu klasu za zajedničku osnovu i deljenu implementaciju, a interfejs za definisanje mogućnosti i granica.

public interface IAsyncRepository<T>
{
    Task<T?> FindAsync(
        int id,
        CancellationToken cancellationToken);
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Generics, Collections and LINQ

10Šta su generički tipovi i zašto su korisni?

Generički tipovi omogućavaju pisanje višekratno iskoristivog koda bezbednog po tipove (type-safe). Oni prebacuju mnoge provere u vreme kompajliranja, uklanjaju potrebu za ručnim konverzijama tipova i mogu izbeći omotavanje (boxing) za vrednosne tipove. Generička ograničenja (generic constraints) definišu šta parametar tipa mora da podržava, kao što je to da bude klasa, struktura, implementacija interfejsa, bazna klasa, neupravljani tip ili da poseduje javni konstruktor bez parametara.

public T? Find<T>(
    IEnumerable<T> items,
    Predicate<T> predicate)
{
    return items.FirstOrDefault(item => predicate(item));
}

public T Create<T>() where T : class, new()
{
    return new T();
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

11Šta su kovarijansa i kontravarijansa?

Kovarijansa omogućava kodu da koristi specifičniji tip tamo gde se očekuje opštiji tip koji se proizvodi, kao što je `IEnumerable<string>` umesto `IEnumerable<object>`. Označava se sa `out` na interfejsima i delegatima. Kontravarijansa ide u suprotnom smeru za vrednosti koje se prihvataju i označava se sa `in`. Varijansa radi samo za interfejse i delegate sa referentnim tipovima; `IEnumerable<int>` ne može postati `IEnumerable<object>` jer bi to zahtevalo omotavanje (boxing), a `List<string>` se ne može dodeliti tipu `List<object>`.

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // covariance

Action<object> printObject = Console.WriteLine;
Action<string> printString = printObject; // contravariance

interface IProducer<out T> { T Produce(); }
interface IConsumer<in T> { void Consume(T value); }
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

12Koja je razlika između IEnumerable<T> i IQueryable<T>?

`IEnumerable<T>` predstavlja .NET enumeraciju, a LINQ operatori obično prihvataju delegate kao što je `Func<T, bool>`. `IQueryable<T>` čuva stabla izraza (expression trees), na primer `Expression<Func<T, bool>>`, koja provajder kao što je Entity Framework Core može prevesti u SQL. Prerano pozivanje metode `ToList` materijalizuje podatke i prebacuje naknadno filtriranje u memoriju. Takođe, provajder upita ne može prevesti svaki C# izraz. Izbegavajte curenje `IQueryable` interfejsa kroz sve slojeve aplikacije, osim ako to nije namerna arhitektonska odluka.

IEnumerable<User> loaded = loadedUsers;
var inMemoryAdults = loaded.Where(x => x.Age >= 18);

IQueryable<User> query = dbContext.Users;
var adults = await query
    .Where(x => x.Age >= 18)
    .ToListAsync();
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

13Šta je odloženo izvršavanje u LINQ (Language Integrated Query) tehnologiji?

Mnogi LINQ operatori grade upit bez njegovog trenutnog izvršavanja. Izvršavanje počinje tek kada se kroz upit iterira (enumeriše) ili kada se pozove završna operacija kao što su ToList, Count ili First. To znači da promene na izvoru podataka pre enumeracije mogu uticati na rezultate, ponovljena enumeracija može ponoviti rad ili SQL upite, a izuzeci mogu nastati tokom enumeracije umesto prilikom same konstrukcije upita.

var query = numbers.Where(x => x > 10);

numbers.Add(42);

foreach (var number in query)
{
    Console.WriteLine(number);
}

var materialized = query.ToList();
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

14Koja je razlika između array, List<T>, Dictionary<TKey,TValue> i HashSet<T>?

Niz (array) ima fiksnu veličinu i brz indeksirani pristup. List<T> je dinamički niz sa indeksiranim pristupom složenosti O(1); Add na kraju ima amortizovanu složenost O(1) jer povećanje kapaciteta alocira veći niz i kopira postojeće elemente, dok je umetanje u sredinu O(n). Dictionary<TKey,TValue> preslikava ključeve u vrednosti i obično pruža pretragu po ključu u O(1). HashSet<T> čuva jedinstvene elemente i idealan je za proveru pripadnosti skupu. Za kolekcije zasnovane na heširanju, ispravne implementacije metoda Equals i GetHashCode su ključne.

var ids = new HashSet<int> { 1, 2, 3 };

if (ids.Contains(userId))
{
    // Fast membership check
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

15Koja je razlika između IEnumerable<T>, ICollection<T>, IList<T> i IReadOnlyCollection<T>?

IEnumerable<T> obezbeđuje samo sekvencijalnu enumeraciju. ICollection<T> dodaje Count i metode za izmenu kao što su Add i Remove. IList<T> dodaje indeksirani pristup. IReadOnlyCollection<T> izlaže enumeraciju i Count, ali ne i izmene kroz taj interfejs. Pri dizajniranju API (Application Programming Interface) rešenja treba vraćati najuži koristan ugovor, ali interfejsi samo za čitanje ne garantuju duboku nepromenljivost osnovnog objekta.

private readonly List<Order> _orders = new();

public IReadOnlyCollection<Order> Orders => _orders;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

16Kako funkcioniše yield return?

yield return kreira iterator sa odloženim izvršavanjem. Kompajler transformiše metodu u mašinu stanja, a vrednosti se generišu jedna po jedna kada se pozove MoveNext. Time se izbegava pravljenje međukolekcija i omogućava efikasan rad sa velikim sekvencama. Zamke se odnose na životni vek i višestruko numerisanje (multiple enumeration): nemojte vraćati lenju sekvencu koja zavisi od uništenog objekta DbContext ili strima, i imajte na umu da poziv Count praćen foreach petljom može izvršiti iterator dva puta, ponavljajući sporedne efekte ili I/O operacije.

IEnumerable<int> GetEvenNumbers(IEnumerable<int> source)
{
    foreach (var number in source)
    {
        if (number % 2 == 0)
            yield return number;
    }
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Delegates, Events and Exceptions

17Šta su delegati i lambda izrazi?

Delegat je tipski bezbedna referenca na metodu. Lambda izrazi pružaju koncizne ugrađene funkcije i najčešće se dodeljuju delegatima Func, Action ili Predicate. Lambde hvataju promenljive, a ne trenutne vrednosti, pa lambda može očitati kasniju vrednost promenljive ili vrednost iz petlje. Ako lambda ništa ne hvata, kompajler može keširati statički delegat i izbeći alokaciju zatvorenja (closure); u delovima koda kritičnim za performanse, zatvorenja i produženi životni vek objekata mogu biti važni.

public delegate int Operation(int x, int y);

Operation operation = (x, y) => x + y;
Console.WriteLine(operation(2, 3));

int factor = 10;
Func<int, int> multiply = x => x * factor;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

18Kako se event razlikuje od delegate u C#-u?

Događaj (event) se obično zasniva na delegatu (delegate), ali ograničava šta spoljni kod može da uradi. Spoljni kod može samo da se pretplati ili odjavi, dok isključivo klasa vlasnik može da oine/pokrene događaj. Javno polje tipa delegata omogućilo bi pozivaocima da zamene listu pretplatnika ili da ga direktno pozovu. Događaji takođe stvaraju reference od izdavača ka pretplatniku, pa dugoživući izdavači mogu izazvati curenje memorije ako se pretplatnici ne odjave.

public event EventHandler<OrderEventArgs>? OrderCreated;

service.OrderCreated += Handler;
service.OrderCreated -= Handler;

OrderCreated?.Invoke(this, args);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

19Kako bi trebalo rukovati izuzecima u C# jeziku?

Koristite izuzetke za izuzetne situacije, a ne za uobičajeni tok izvršavanja programa. Hvatajte samo one izuzetke kojima možete da upravljate, izbegavajte prazne `catch` blokove, dodajte kontekst prilikom obmotavanja izuzetaka i nemojte otkrivati interne detalje korisnicima. Koristite `throw;` za ponovno izazivanje izuzetka uz očuvanje originalnog stabla poziva. Ako obmotate izuzetak, prosledite originalni kao `InnerException`, na primer `throw new DomainException("...", ex)`. Dajte prednost API-jima u stilu `TryParse` za očekivane neuspehe.

try
{
    await service.ProcessAsync();
}
catch (ValidationException exception)
{
    // Expected domain error
}
finally
{
    // Cleanup
}

catch
{
    throw; // preserves stack trace
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

20Šta su filteri izuzetaka (exception filters)?

Filteri izuzetaka omogućavaju da se `catch` blok izvrši samo kada je dodatni uslov ispunjen. Filter se evaluira pre odmotavanja steka i pre ulaska u telo `catch` bloka, pa kada vrati `false`, originalni stek ostaje netaknut za dijagnostiku i izveštaje o padu programa. Filteri su korisni za HTTP (Hypertext Transfer Protocol) statusne kodove, kodove grešaka baza podataka, razlikovanje prolaznih grešaka i uslovno evidentiranje. Održavajte filtere jednostavnim i izbegavajte složene sporedne efekte.

try
{
    await SendAsync();
}
catch (HttpRequestException ex)
    when (ex.StatusCode == HttpStatusCode.NotFound)
{
    // Handle only 404
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom