Bộ câu hỏi phỏng vấn C#/.Net phần 4

Virtual Method trong C# là gì?

Virtual Method (phương thức ảo) là một phương thức có thể được định nghĩa lại trong các lớp dẫn xuất. Một virtual method có một triển khai trong lớp cơ sở cũng như lớp dẫn xuất. Khi một virtual method được gọi, kiểu trong runtime của đối tượng sẽ được kiểm tra xem có thành phần ghi đè hay không.

public

virtual

double

Area

(

)

{

return

x

*

y

;

}

  • Theo mặc định, các phương thức là non-virtual. Chúng ta không thể ghi đè một phương thức non-virtual.
  • Chúng ta không thể sử dụng công cụ sửa đổi virtual với các công cụ sửa đổi static, abstract, private hoặc override.

Công dụng của using trong C# là gì?

Nguyên nhân sử dụng câu lệnh using là để đảm bảo rằng đối tượng được giải phóng tài nguyên (gọi IDisposable) ngay khi nó vượt ra khỏi phạm vi và nó không yêu cầu mã rõ ràng để đảm bảo rằng điều này (việc giải phóng tài nguyên) xảy ra. .NET CLR chuyển đổi:

using

(

MyResource

myRes

=

new

MyResource

(

)

)

{

myRes

.

DoSomething

(

)

;

}

thành:

{

MyResource

myRes

=

new

MyResource

(

)

;

try

{

myRes

.

DoSomething

(

)

;

}

finally

{

if

(

myRes

!=

null

)

(

(

IDisposable

)

myRes

)

.

Dispose

(

)

;

}

}

Giải thích kiểu Anonymous trong C#?

Kiểu Anonymous cho phép chúng ta tạo một kiểu mới mà không cần định nghĩa chúng. Đây là cách để xác định các thuộc tính read-only vào một đối tượng duy nhất mà không cần phải định nghĩa kiểu rõ ràng. Ở đây Type được tạo bởi trình biên dịch và nó chỉ có thể truy cập được đối với khối mã hiện tại. Loại thuộc tính cũng được suy ra bởi trình biên dịch.

var

anonymousData

=

new

{

ForeName

=

"Jignesh"

,

SurName

=

"Trivedi"

}

;

Console

.

WriteLine

(

"First Name : "

+

anonymousData

.

ForeName

)

;

Tại sao bạn không thể chỉ định accessibility modifier cho các phương thức bên trong Interface?

Trong một interface, chúng ta có các phương thức ảo (virtual method) không có định nghĩa phương thức. Tất cả các phương thức ở đó sẽ được ghi đè trong lớp dẫn xuất. Đó là lý do tại sao tất cả chúng đều là public.

Sự khác biệt giữa Virtual method và Abstract method là gì?

  • Virtual method luôn phải có một triển khai mặc định. Tuy nhiên, nó có thể được ghi đè trong lớp kế thừa, mặc dù không bắt buộc. Nó có thể được ghi đè bằng cách sử dụng từ khóa override.
  • Abstract method không có phần triển khai. Nó nằm trong abstract class. Lớp kế thừa bắt buộc phải triển khai abstract method. Một từ khóa override là không cần thiết ở đây mặc dù nó có thể được sử dụng.

Sự khác biệt giữa biến kiểu dynamic và biến kiểu object là gì?

  • Kiểu object là một alias (bí danh) cho System.Object trong .NET. Trong hệ thống kiểu thống nhất của C#, tất cả các kiểu, được xác định trước và do người dùng định nghĩa, kiểu tham chiếu và kiểu giá trị, đều kế thừa trực tiếp hoặc gián tiếp từ System.Object. Bạn có thể gán giá trị của bất kỳ kiểu nào cho các biến kiểu object.
  • Kiểu dynamic chỉ ra rằng việc sử dụng biến và các tham chiếu đến các thành viên của nó sẽ bỏ qua việc kiểm tra kiểu compile-time. Thay vào đó, các hoạt động này được giải quyết tại runtime. Kiểu dynamic đơn giản hóa quyền truy cập vào các COM APIs như Office Automation APIs, tới các dynamic API như thư viện IronPython và vào HTML Document Object Model (DOM).
  • Kiểu dynamic hoạt động giống như kiểu object trong hầu hết các trường hợp. Đặc biệt, bất kỳ biểu thức non-null nào cũng có thể được chuyển đổi thành kiểu dynamic. Kiểu dynamic khác với object ở chỗ các thao tác có chứa biểu thức kiểu dynamic không được giải quyết hoặc kiểm tra kiểu bởi trình biên dịch. Trình biên dịch đóng gói thông tin về hoạt động và thông tin này sau đó được sử dụng để đánh giá hoạt động tại runtime. Là một phần của quá trình, các biến của kiểu dynamic được biên dịch thành các biến của kiểu object. Do đó, kiểu dynamic chỉ tồn tại ở thời gian biên dịch, không tồn tại ở runtime.

Giả sử chúng ta có phương thức sau:

public

static

void

ConsoleWrite

(

string

inputArg

)

{

Console

.

WriteLine

(

inputArg

)

;

}

Object – đoạn mã sau có lỗi biên dịch trừ khi truyền đối tượng thành chuỗi:

public

static

void

Main

(

string

[

]

args

)

{

object

obj

=

"String Sample"

;

ConsoleWrite

(

obj

)

;

ConsoleWrite

(

(

string

)

obj

)

;

Console

.

ReadKey

(

)

;

}

Dynamic – mã sau đây biên dịch thành công nhưng nếu nó chứa một giá trị không phải string, nó sẽ tạo ra lỗi Runtime.

public

static

void

Main

(

string

[

]

args

)

{

dynamic

dyn

=

"String Sample"

;

ConsoleWrite

(

dyn

)

;

dyn

=

1

;

ConsoleWrite

(

dyn

)

;

Console

.

ReadKey

(

)

;

}

Đóng gói được thực hiện như thế nào trong C#?

Việc đóng gói (encapsulation) được thực hiện bằng cách sử dụng các access specifiers. Một access specifier xác định phạm vi và khả năng hiển thị của một thành viên của lớp.

  • Public access specifier cho phép một lớp hiển thị các biến thành viên và các hàm thành viên của nó với các hàm và đối tượng khác. Mọi thành viên public đều có thể được truy cập từ bên ngoài lớp.
  • Private access specifier cho phép một lớp ẩn các biến thành viên và các hàm thành viên của nó khỏi các hàm và đối tượng khác. Chỉ các hàm của cùng một lớp mới có thể truy cập các thành viên private của nó. Ngay cả một instance của một lớp cũng không thể truy cập vào các thành viên private của nó.
  • Protected access specifier cho phép một lớp con truy cập các biến thành viên và các hàm thành viên của lớp cơ sở của nó. Bằng cách này, nó sẽ giúp thực hiện kế thừa.

Toán tử Null Coalescing (??) được sử dụng như thế nào trong C#?

Toán tử ?? được gọi là toán tử Null Coalescing và được sử dụng để định nghĩa một giá trị mặc định cho các kiểu giá trị nullable hoặc kiểu tham chiếu. Nó trả về toán hạng bên trái nếu toán hạng không null; ngược lại, nó trả về toán hạng bên phải.

Biểu thức lambda trong C# là gì?

Biểu thức lambda là một hàm anonymous mà bạn có thể sử dụng để tạo delegate hoặc kiểu cây biểu thức. Bằng cách sử dụng biểu thức lambda, bạn có thể viết các hàm cục bộ có thể được truyền dưới dạng đối số, hoặc trả về dưới dạng giá trị của lời gọi hàm. Biểu thức lambda đặc biệt hữu ích để viết biểu thức truy vấn LINQ.

Trong ví dụ sau, biểu thức lambda x => x * x, chỉ định một tham số có tên là x và trả về giá trị của x bình phương, được gán cho một biến kiểu delegate:

Func

<

int

,

int

>

square

=

x

=>

x

*

x

;

Console

.

WriteLine

(

square

(

5

)

)

;

Làm thế nào để một lớp không bị ghi đè trong C#?

Bạn có thể ngăn một lớp khỏi bị ghi đè trong C# bằng cách sử dụng từ khóa sealed.

Có cách nào để bắt nhiều exception cùng một lúc và không phải duplicate mã không?

Vấn đề:

try

{

WebId

=

new

Guid

(

queryString

[

"web"

]

)

;

}

catch

(

FormatException

)

{

WebId

=

Guid

.

Empty

;

}

catch

(

OverflowException

)

{

WebId

=

Guid

.

Empty

;

}

Có cách nào để bắt cả hai exception và chỉ gọi lệnh gọi WebId = Guid.Empty một lần không? Giải pháp:

catch

(

Exception

ex

)

{

if

(

ex

is

FormatException

||

ex

is

OverflowException

)

{

WebId

=

Guid

.

Empty

;

return

;

}

throw

;

}

Tại sao cần sử dụng IDisposable interface?

Công dụng “chính” của IDisposable interface là dọn dẹp các tài nguyên không được quản lý. Lưu ý rằng mục đích của mẫu Dispose này là cung cấp một cơ chế để dọn dẹp cả tài nguyên được quản lý và không được quản lý và khi nào điều đó xảy ra phụ thuộc vào cách phương thức Dispose được gọi.

Giải thích sự khác biệt giữa Task và Thread trong .NET?

  • Thread đại diện cho một thread cấp hệ điều hành thực tế, với stack và tài nguyên hạt nhân (kernel) của riêng nó. Thread cho phép mức độ kiểm soát cao nhất; bạn có thể Abort() hoặc Suspend() hoặc Resume() một thread, bạn có thể quan sát trạng thái (state) của nó và bạn có thể đặt các thuộc tính cấp thread như stack size, state hoặc culture. ThreadPool là một trình bao bọc xung quanh một nhóm các thread được duy trì bởi CLR.
  • Lớp Task từ Task Parallel Library cung cấp những gì tốt nhất của cả hai thế giới. Giống như ThreadPool, một task không tạo thread hệ điều hành của riêng nó. Thay vào đó, các task được thực thi bởi TaskScheduler; bộ lập lịch mặc định chỉ chạy trên ThreadPool. Không giống như ThreadPool, Task cũng cho phép bạn biết được khi nào nó hoàn thành và (thông qua generic Task) để trả về kết quả.