Extension Method trong C# là gì và cách sử dụng chúng?
Các extension method (phương thức mở rộng) cho phép bạn thêm các phương thức vào các kiểu hiện có mà không cần tạo một kiểu dẫn xuất mới, biên dịch lại hoặc sửa đổi kiểu gốc. Extension method là một loại phương thức static đặc biệt, nhưng chúng được gọi như thể chúng là các phương thức instance trên kiểu mở rộng.
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
Lớp sealed trong C# là gì?
- Khi một lớp được định nghĩa là một lớp
sealed
, thì lớp đó không thể được kế thừa. - Các
struct
cũng đượcsealed
.
Reflection trong C#.Net là gì?
Reflection
là khả năng truy vấn và tương tác với hệ thống kiểu theo cách động (dynamic way). Nói chung, Reflection
cho phép bạn truy cập vào metadata (siêu dữ liệu) về các đối tượng. Ví dụ, bạn có thể tải một DLL và xác định xem nó có chứa phần triển khai của một giao diện hay không. Bạn có thể sử dụng điều này để khám phá các dll hỗ trợ chức năng trong runtime. Bạn có thể sử dụng reflection
để mở rộng một ứng dụng mà không cần biên dịch lại và không cần phải khởi động lại.
Sự khác biệt giữa constant và readonly là gì?
Ngoài sự khác biệt rõ ràng là
- Phải khai báo giá trị tại thời điểm định nghĩa cho một
const
so với các giá trị readonly có thể được tính toán động nhưng cần được gán trước khi phương thức khởi tạo thoát ra ... sau đó nó bị đóng băng (frozen). const
là hoàn toànstatic
. Bạn sử dụng ký hiệuClassName.ConstantName
để truy cập chúng.
Còn có một sự khác biệt tinh tế, hãy xem xét lớp được định nghĩa trong AssemblyA
.
public class Const_V_Readonly
{
public const int I_CONST_VALUE = 2;
public readonly int I_RO_VALUE;
public Const_V_Readonly()
{
I_RO_VALUE = 3;
}
}
AssemblyB
tham chiếu đến AssemblyA
và sử dụng các giá trị này trong mã. Khi điều này được biên dịch,
- Trong trường hợp của giá trị const, nó giống như một tìm kiếm thay thế (find-replace), giá trị 2 được 'đưa vào' IL của
AssemblyB
. Điều này có nghĩa là nếu ngày mai, chúng ta cập nhậtI_CONST_VALUE
trongAssemblyA
thành20
, thìI_CONST_VALUE
trongAssemblyB
vẫn là 2 cho đến khi chúng ta biên dịch lại nó. - Trong trường hợp của giá trị
readonly
, nó giống như một tham chiếu đến một vị trí bộ nhớ. Giá trị không được đưa vào IL củaAssemblyB
. Điều này có nghĩa là nếu vị trí bộ nhớ được cập nhật,AssemblyB
sẽ nhận được giá trị mới mà không cần biên dịch lại. Vì vậy, nếuI_RO_VALUE
được cập nhật thành 30, bạn chỉ cần buildAssemblyA
. Tất cả các client không cần phải được biên dịch lại.
Hãy nhớ rằng nếu bạn tham chiếu một const
từ một assembly
khác, giá trị của nó sẽ được biên dịch ngay trong assembly
đang gọi. Bằng cách đó, khi bạn cập nhật const trong assembly
được tham chiếu, nó sẽ không thay đổi trong assembly
đang gọi!
Giải thích quá trình biên dịch mã trong C#?
Có bốn bước trong quá trình biên dịch mã bao gồm:
- Biên dịch mã nguồn thành Managed code bởi trình biên dịch C#.
- Kết hợp mã mới tạo thành các assembly.
- Tải Common Language Runtime(CLR).
- Thực thi assembly bằng CLR
Phạm vi của biến thành viên Internal của một lớp C# là gì?
Internal 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 trong assembly hiện tại. Nói cách khác, bất kỳ thành viên nào với internal access specifier đều có thể được truy cập từ bất kỳ lớp hoặc phương thức nào được định nghĩa trong cùng ứng dụng.
public class BaseClass
{
// Only accessible within the same assembly.
internal static int x = 0;
}
Bạn có thể sử dụng nó cho các lớp/phương thức tiện ích hoặc trợ giúp mà bạn muốn truy cập từ nhiều lớp khác trong cùng một assembly, nhưng bạn muốn đảm bảo mã trong các assembly khác không thể truy cập.
Hàm anonymous trong C# là gì?
Hàm anonymous (hàm ẩn danh) là một câu lệnh hoặc biểu thức "nội tuyến" có thể được sử dụng ở bất cứ nơi nào mong đợi một kiểu delegate. Bạn có thể sử dụng nó để khởi tạo một delegate đã đặt tên hoặc truyền nó làm tham số phương thức.
Có hai loại hàm anonymous:
Lambda Expressions (biểu thức Lambda)
Anonymous Methods (phương thức ẩn danh)
// Original delegate syntax required
// initialization with a named method.
TestDelegate testDelA = new TestDelegate(M);
// C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes a string as an input parameter.
TestDelegate testDelB = delegate(string s) {
Console.WriteLine(s);
};
// C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda also takes a string
// as an input parameter (x). The type of x is inferred by the compiler.
TestDelegate testDelC = (x) => {
Console.WriteLine(x);
};
// Invoke the delegates.
testDelA("Hello. My name is M and I write lines.");
testDelB("That's nothing. I'm anonymous and ");
testDelC("I'm a famous author.");
Marshalling là gì và tại sao chúng ta cần nó?
Bởi vì các ngôn ngữ và môi trường khác nhau có các quy ước gọi khác nhau, các quy ước bố cục khác nhau, các kích thước primitive khác nhau (ví dụ char trong C# và char trong C), các quy ước tạo / hủy đối tượng khác nhau và các hướng dẫn thiết kế khác nhau. Bạn cần một cách để đưa những thứ từ môi trường được quản lý vào một nơi nào đó mà môi trường không được quản lý có thể nhìn thấy và hiểu nó và ngược lại. Đó là những gì mà Marshalling thực hiện.
Marshaling là quá trình tạo cầu nối giữa managed code và unmanaged code, Marshalling là một trong những dịch vụ quan trọng nhất do CLR cung cấp.
Sự khác biệt giữa phương thức dispose và finalize là gì trong C#?
Cả hai Finalize và Dispose đều được sử dụng cho cùng một nhiệm vụ giải phóng unmanaged resource (tài nguyên không được quản lý) nhưng có một số điểm khác biệt.
Finalize:
Finalize
được sử dụng để giải phóng các unmanaged resource không được sử dụng như tệp, kết nối cơ sở dữ liệu trong miền ứng dụng, ... được giữ bởi một đối tượng trước khi đối - tượng đó bị phá hủy.- Trong quy trình nội bộ (Internal process), nó được gọi bởi
Garbage Collector
và không thể gọi thủ công bằng mã người dùng hoặc bất kỳ service nào. Finalize
thuộc về lớpSystem.Object
.- Triển khai nó khi bạn có unmanaged resource trong mã của mình và đảm bảo rằng những tài nguyên này được giải phóng khi quá trình thu gom rác diễn ra.
Dispose:
Dispose
cũng được sử dụng để giải phóng cácunmanaged resource
không được sử dụng như tệp, kết nối cơ sở dữ liệu trong miền ứng dụng bất cứ lúc nào.Dispose
được gọi thủ công bằng mã người dùng một cách rõ ràng.- Nếu chúng ta cần phương thức
dispose
thì phải triển khai lớp đó bằngIDisposable interface
. Dispose
thuộc vềIDisposable interface
.- Triển khai dispose khi bạn đang viết một custom class để được sử dụng bởi những người dùng khác.
Sự khác biệt giữa late binding và early binding trong C#?
- Trong Compile time polymorphism (đa hình thời gian biên dịch) hay gọi là Early Binding (liên kết sớm), chúng ta sẽ sử dụng nhiều phương thức có cùng tên nhưng khác tham số, bởi vì điều này mà chúng ta có thể thực hiện các tác vụ khác nhau với cùng một tên phương thức trong cùng một lớp và nó được gọi là Method overloading.
- Run time polymorphism (đa hình thời gian chạy) còn được gọi là Late Binding (liên kết muộn). Trong Late Binding, chúng ta có thể sử dụng cùng một tên phương thức với cùng chữ ký có nghĩa là cùng kiểu hoặc cùng số lượng tham số nhưng không cùng lớp vì trình biên dịch không cho phép điều đó tại thời điểm biên dịch, vì vậy chúng ta có thể sử dụng trong lớp kế thừa liên kết tại runtime khi một đối tượng của lớp con hoặc lớp kế thừa được khởi tạo, đó là cách chúng ta nói rằng Late Binding còn được gọi là Method overriding.
Constructor Chaining trong C# là gì?
Constructor Chaining là một cách tiếp cận mà một phương thức khởi tạo (constructor) gọi một phương thức khởi tạo khác trong cùng một lớp cơ sở hoặc lớp cơ sở. Điều này rất hữu ích khi chúng ta có một lớp định nghĩa nhiều phương thức khởi tạo.
class Foo {
private int id;
private string name;
public Foo() : this(0, "") {
}
public Foo(int id) : this(id, "") {
}
public Foo(string name) : this(0, name) {
}
public Foo(int id, string name) {
this.id = id;
this.name = name;
}
}
Sự khác biệt giữa toán tử is và as trong C# là gì?
Toán tử is kiểm tra xem một đối tượng có thể được ép thành một kiểu cụ thể hay không.
if (someObject is StringBuilder) ...
Toán tử as cố gắng ép một đối tượng thành một kiểu cụ thể và trả về null nếu nó không thành công.
StringBuilder b = someObject as StringBuilder;
if (b != null) ...
Bạn có thể tạo một hàm trong C# có thể chấp nhận số lượng đối số khác nhau không?
Bằng cách sử dụng từ khóa params, bạn có thể chỉ định một tham số của phương thức nhận một số lượng đối số thay đổi. Không có tham số bổ sung nào khác được phép sau từ khóa params trong khai báo phương thức và chỉ một từ khóa params được phép trong khai báo phương thức. Kiểu khai báo của tham số params phải là mảng một chiều.
public static void UseParams(params int[] list) {
for (int i = 0; i < list.Length; i++) {
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
public static void UseParams2(params object[] list) {
for (int i = 0; i < list.Length; i++) {
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
// usage
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");
(senior)
Operator overloading (nạp chồng toán tử) có được hỗ trợ trong C# không?
Kiểu do người dùng xác định có thể nạp chồng toán tử được xác định trước trong C#. Có nghĩa là, một kiểu có thể cung cấp triển khai tùy chỉnh của một toán tử trong trường hợp một hoặc cả hai toán hạng thuộc kiểu đó.
public static Box operator+ (Box b, Box c) {
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
Hàm trên thực hiện toán tử cộng (+) cho một lớp Box do người dùng định nghĩa. Nó thêm các thuộc tính của hai đối tượng Box và trả về đối tượng Box kết quả.