Câu hỏi phỏng vấn Generic
A. Cơ bản
1. Generic là gì? Giải quyết vấn đề gì?
Parametric polymorphism — viết code làm việc với nhiều type mà giữ type safety. Lợi ích:
- Type safety compile-time.
- Code reuse (không duplicate cho từng type).
- Performance (C# value type không boxing).
- Self-documenting.
2. Generic khác Inheritance polymorphism chỗ nào?
- Inheritance (subtype polymorphism): runtime, qua vtable.
- Generic (parametric polymorphism): compile-time check, có thể combine với subtype (constraint).
3. Generic function vs Generic class — khác gì?
- Function: type param chỉ trong scope function.
T Identity<T>(T x). - Class: type param share toàn class.
Box<T>— mọi instance có T cụ thể.
4. Type inference trong generic?
Compiler suy luận type param từ argument. Identity(42) → T = int. Khi không inferred được → phải specify thủ công.
5. Generic method trong non-generic class?
Class non-generic vẫn có thể có method generic riêng:
class Cache {
public void Set<T>(string key, T value) { ... }
}
B. Constraint
6. Constraint là gì?
Hạn chế type parameter T phải thoả điều kiện — implement interface, extend class, có constructor, là value/ref type, v.v.
7. Liệt kê constraint C# phổ biến?
where T : class— reference type.where T : struct— value type.where T : new()— có default ctor.where T : IInterface— implement interface.where T : BaseClass— kế thừa.where T : Enum— enum (C# 7.3+).where T : INumber<T>— static interface (C# 11+).where T : U— linked với U.
8. TS constraint với extends ra sao?
function len<T extends { length: number }>(x: T) { return x.length; }
function get<T, K extends keyof T>(o: T, k: K): T[K] { return o[k]; }
9. keyof T trong TS là gì?
Union các key của T dưới dạng string literal type:
keyof { id: number; name: string } // "id" | "name"
10. T[K] indexed access?
Lấy value type theo key: User["name"] = string. Combine với keyof cho type-safe access.
11. Generic constraint với keyof — vì sao mạnh?
Type-safe property access — compile-time check key tồn tại + return đúng type:
function get<T, K extends keyof T>(obj: T, key: K): T[K]
12. Multi constraint?
C#: where T : class, IDisposable, new(). TS: T extends Iterable<U> & { length: number }.
C. Variance
13. Variance là gì? 3 loại?
Quan hệ subtype giữa generic instantiation:
- Covariant (
out): nếu Dog : Animal thìF<Dog> : F<Animal>. Producer. - Contravariant (
in): F: F . Consumer. - Invariant (default): không quan hệ.
14. Vì sao List<T> không covariant?
Có cả Add(T) (input) và this[i] (output). Nếu covariant → có thể Add Cat vào List<Animal> chứa Dog → crash. Mixed → phải invariant.
15. Vì sao IEnumerable<T> covariant?
T chỉ ở output (return của GetEnumerator/Current). Không Add được. Safe để covariant — IEnumerable<Dog> assignable sang IEnumerable<Animal>.
16. Vì sao IComparer<T> contravariant?
T chỉ ở input của Compare. Comparer so sánh được Animal → cũng so sánh được Dog (Dog là Animal). Đảo chiều.
17. Mnemonic variance?
"out → cùng hướng (covariant), in → ngược hướng (contravariant)". Producer covariant, Consumer contravariant.
18. TS variance — auto hay explicit?
Mặc định auto-inferred từ usage. TS 4.7+ thêm explicit out/in annotation nhưng không bắt buộc.
19. Bivariant function param trong TS?
TS mặc định cho function param bivariant (vừa cov vừa contra) — không strict. Bật strictFunctionTypes: true → chỉ contravariant (đúng lý thuyết).
20. Function variance — return vs param?
- Return covariant (subtype).
- Param contravariant (supertype).
(Animal => Dog) assignable sang (Dog => Animal) vì:
- Return Dog : Animal ✅
- Param Animal supertype của Dog ✅
D. C# vs TS
21. Reified vs Erased generic?
- Reified (C#, Java modern): runtime giữ type info.
typeof(List<int>) != typeof(List<string>). - Erased (TS, Java legacy): generic biến mất sau compile.
22. Khác biệt thực tế reified vs erased?
C# có thể:
new T()(với new() constraint).typeof(T)runtime.- Reflection introspect generic.
- Overload theo generic type.
TS không có những thứ trên — phải workaround.
23. Trong TS, tạo instance từ generic type sao?
Nhận constructor làm param:
function create<T>(Ctor: new () => T): T { return new Ctor(); }
24. C# generic không boxing — ý nghĩa?
List<int> lưu int trực tiếp, không wrap thành object trên heap. Compile sinh IL specialized cho mỗi value type → fast. ArrayList thì box mỗi int.
25. Generic + reflection trong C#?
var type = typeof(List<>).MakeGenericType(typeof(string));
var inst = Activator.CreateInstance(type);
Tạo type generic runtime — power lớn cho ORM, DI container.
E. Advanced TS
26. Conditional type là gì?
T extends U ? X : Y — chọn type theo điều kiện. Cơ sở của mọi utility type advanced.
27. Distributive conditional?
Khi T là naked type param + T là union → conditional apply cho từng member:
type ToArr<T> = T extends any ? T[] : never;
type R = ToArr<string | number>; // string[] | number[]
28. Chống distribute?
Wrap T trong tuple: [T] extends [U] ? ... : ....
29. infer keyword?
Bắt type trong nhánh conditional:
type Return<F> = F extends (...a: any) => infer R ? R : never;
30. Mapped type?
Tạo type mới bằng map qua keys:
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
31. Key remapping (TS 4.1+)?
[K in keyof T as NewKey]:
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
32. Template literal type?
String literal có placeholder: `Hello, ${string}!`. Combine với union để sinh permutation.
F. Real-world
33. Implement Repository generic trong C#?
public abstract class Repository<T> where T : class, IEntity, new() {
protected DbContext _ctx;
public Repository(DbContext ctx) => _ctx = ctx;
public Task<T?> GetByIdAsync(int id) => _ctx.Set<T>().FindAsync(id).AsTask();
public async Task AddAsync(T e) { _ctx.Set<T>().Add(e); await _ctx.SaveChangesAsync(); }
}
34. Implement type-safe API client trong TS?
async function fetchJson<T>(url: string, schema: ZodType<T>): Promise<T> {
const data = await fetch(url).then(r => r.json());
return schema.parse(data); // runtime validate
}
35. Self-referencing generic (CRTP)?
public abstract class Builder<TSelf> where TSelf : Builder<TSelf> {
public TSelf SetName(string n) { ... return (TSelf)this; }
}
public class UserBuilder : Builder<UserBuilder> {
public UserBuilder SetEmail(string e) { ... return this; }
}
→ Fluent chain giữ đúng concrete type.
36. Type-safe Object.keys trong TS?
Object.keys trả string[]. Wrap:
function keys<T extends object>(o: T): (keyof T)[] {
return Object.keys(o) as (keyof T)[];
}
37. Branded type cho ID?
type UserId = number & { __brand: "UserId" };
type OrderId = number & { __brand: "OrderId" };
Chống nhầm UserId với OrderId dù cùng number.
G. Câu trick
38. Generic và default(T) — trả gì?
C#: default(T) = null nếu T ref type, 0 cho int, default struct cho struct.
TS: không có cú pháp tương đương — thường khởi null hoặc undefined.
39. where T : class? C# nghĩa là gì?
T là nullable reference type (C# 9+). where T : class thì T là non-null ref. ? cho phép null.
40. Tại sao TS Promise<T> covariant?
T chỉ xuất hiện ở output (.then(value: T)). Producer → covariant. Promise<Dog> assignable sang Promise<Animal>.
41. C# Action<T> vs Func<T> variance?
Action<in T>: T input → contravariant.Func<out T>: T return → covariant.Func<in T, out R>: input contra, output cov.
42. Tại sao Array<T> trong TS hoạt động như covariant?
TS không enforce variance trên built-in Array do legacy + bivariant default. Strict mode → coi như covariant nhưng vẫn cho phép push (unsoundness có ý).
43. C# 11 generic math là gì?
Static interface (INumber
public static T Sum<T>(IEnumerable<T> nums) where T : INumber<T> =>
nums.Aggregate(T.Zero, (a, b) => a + b);
44. out keyword trong C# generic interface — khác out parameter?
Khác hoàn toàn:
out Ttronginterface IEnumerable<out T>: variance annotation (covariant).out T xtrong method parameter: parameter modifier (must assign in method).
Cùng keyword, ngữ cảnh khác.
45. Erased generic gây "type leak" không?
Có thể. Ví dụ TS instanceof Array không phân biệt Array<string> và Array<number>. → Cần custom guard hoặc schema validation runtime.