Boxing và Unboxing là gì?
Boxing: Chuyển đổi một giá trị của một kiểu dữ liệu giá trị (ví dụ: int, float, double) thành một kiểu dữ liệu tham chiếu (ví dụ: object). Việc này xảy ra khi bạn cần lưu trữ một giá trị giữa các collection như ArrayList hoặc Hashtable, mà yêu cầu các phần tử của chúng phải là các đối tượng tham chiếu.
Unboxing: Chuyển đổi một đối tượng kiểu dữ liệu tham chiếu (ví dụ: object) thành một kiểu dữ liệu giá trị (ví dụ: int, float, double). Điều này là cần thiết khi bạn muốn trích xuất giá trị từ một đối tượng đã được boxing và sử dụng nó như một giá trị của kiểu dữ liệu gốc.
Ví dụ:
// Boxing: int i = 123; object obj = i;
// Unboxing: int int j = (int)obj;
Sự khác biệt giữa Struct và Class?
Đặc điểm |
Struct |
Class |
Bản chất |
Kiểu dữ liệu giá trị (value type) |
Kiểu dữ liệu tham chiếu (reference type) |
Kế thừa |
Không hỗ trợ kế thừa |
Hỗ trợ kế thừa |
Hiệu suất |
Thích hợp cho lưu trữ dữ liệu nhỏ, kiểu giá trị |
Thích hợp cho đại diện thực thể phức tạp, có thể thay đổi kích thước |
Sao chép |
Khi sao chép, tạo ra một bản sao mới của dữ liệu |
Khi sao chép, tạo ra một tham chiếu mới đến cùng một đối tượng, không phải là một bản sao mới của dữ liệu |
This có thể được sử dụng trong một phương thức Static không?
Không. Vì trong phương thức static không có đối tượng hiện tại cụ thể của lớp.
Phương thức static là một phương thức thuộc về lớp chứ không phải thuộc về một đối tượng cụ thể của lớp đó. Điều này có nghĩa là không có đối tượng cụ thể được tạo ra để tham chiếu đến trong phương thức static, do đó từ khóa "this" không thể được sử dụng.
So sánh string và StringBuilder?
-
Immutable vs Mutable:
- string: Là một kiểu dữ liệu bất biến (immutable), có nghĩa là một khi một chuỗi đã được tạo ra, nó không thể thay đổi. Mọi thao tác thay đổi hoặc biến đổi chuỗi sẽ tạo ra một chuỗi mới.
- StringBuilder: Là một lớp có thể thay đổi (mutable), cho phép thay đổi nội dung của chuỗi mà không tạo ra chuỗi mới. Điều này làm giảm bớt số lần cấp phát bộ nhớ và tăng hiệu suất khi thực hiện nhiều thao tác trên chuỗi.
-
Hiệu suất:
- string: Khi thực hiện các thao tác như cắt, nối, hoặc thay đổi nội dung của chuỗi, sẽ tạo ra các bản sao mới của chuỗi, điều này có thể ảnh hưởng đến hiệu suất và tốn thời gian.
- StringBuilder: Được tối ưu để thực hiện các thao tác cắt, nối và thay đổi nội dung của chuỗi, mà không cần tạo ra các bản sao mới. Do đó, StringBuilder thường nhanh hơn và tiêu tốn ít bộ nhớ hơn khi thực hiện các thao tác phức tạp trên chuỗi.
-
Sử dụng:
- string: Thích hợp khi làm việc với các chuỗi không thay đổi, như các thông điệp, hằng số, hoặc dữ liệu tĩnh.
- StringBuilder: Thích hợp khi cần thực hiện các thao tác thay đổi nội dung của chuỗi nhiều lần, như khi xây dựng các chuỗi dựa trên dữ liệu động hoặc thao tác nhiều lần trên chuỗi lớn.
Có thể thực thi nhiều khối lệnh catch không?
Có. Tuy nhiên, một lưu ý quan trọng là mỗi khối catch
chỉ thực thi một lần duy nhất. Sau khi một ngoại lệ đã được so khớp với một khối catch
và thực thi nó, chương trình sẽ tiếp tục thực hiện từ phía dưới khối catch
và không quay lại kiểm tra các khối catch
khác.
Ví dụ:
using System;
class Program
{
static void Main()
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Gây ra ngoại lệ IndexOutOfRangeException
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Index out of range exception occurred.");
}
catch (Exception)
{
Console.WriteLine("Generic exception occurred.");
}
finally
{
Console.WriteLine("Finally block executed.");
}
}
}
LINQ trong C # là gì?
LINQ là viết tắt của "Language Integrated Query", là một tính năng của ngôn ngữ lập trình C# cho phép truy vấn và thao tác dữ liệu từ nhiều nguồn khác nhau bằng cách sử dụng cú pháp tương tự như SQL.
LINQ giúp làm cho việc truy vấn và xử lý dữ liệu trở nên dễ dàng, rõ ràng và linh hoạt hơn trong các ứng dụng C#.
LINQ cung cấp một cách thức thống nhất để truy vấn dữ liệu từ các nguồn dữ liệu khác nhau như:
- Các cấu trúc dữ liệu c# như danh sách (List), mảng (Array), bộ sưu tập (Collection), và dictionary.
- Các bảng cơ sở dữ liệu quan hệ (SQL Server, MySQL, SQLite, v.v.).
- XML.
- Các nguồn dữ liệu khác nhau như JSON, CSV, v.v.
LINQ cho phép viết các truy vấn dữ liệu bằng cách sử dụng các phương thức mở rộng (extension methods) và các biểu thức lambda để thực hiện các hoạt động như lọc (where), sắp xếp (orderby), nhóm (group by), liên kết (join), và tính toán (select, aggregate).
Abstract class là gì?
Abstract class là một lớp mà không thể tạo ra các đối tượng (instances) từ nó, nhưng nó có thể chứa các phương thức trừu tượng (abstract methods), các phương thức ảo (virtual methods), các thuộc tính và các biến dữ liệu.
Abstract class thường được sử dụng để định nghĩa một bản thiết kế chung cho các lớp con (derived classes) và định nghĩa các phương thức hoặc thuộc tính chung mà các lớp con có thể triển khai hoặc ghi đè.
Một số đặc điểm chính của abstract class là:
-
Không thể tạo đối tượng: Bạn không thể tạo ra các đối tượng từ một abstract class bằng cách sử dụng từ khóa new
. Abstract class được thiết kế để được kế thừa và triển khai bởi các lớp con.
-
Có thể chứa các phương thức trừu tượng: Abstract class có thể chứa các phương thức trừu tượng mà không cần cung cấp cài đặt. Các phương thức trừu tượng này được kế thừa bởi các lớp con và phải được triển khai trong các lớp con.
-
Có thể chứa các phương thức ảo (virtual methods): Abstract class cũng có thể chứa các phương thức ảo, cho phép các lớp con ghi đè lên chúng nếu cần.
-
Có thể chứa các thuộc tính và các biến dữ liệu: Abstract class cũng có thể chứa các thuộc tính và các biến dữ liệu, giúp định nghĩa các trạng thái hoặc hành vi chung cho các lớp con.
Ví dụ:
abstract class Animal
{
public abstract void MakeSound();
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow!");
}
}
class Program
{
static void Main()
{
Animal dog = new Dog();
dog.MakeSound(); // Output: Woof!
Animal cat = new Cat();
cat.MakeSound(); // Output: Meow!
}
}
Partial class là gì?
Trong C#, Partial Class (lớp một phần) là một tính năng cho phép bạn chia một lớp thành nhiều phần, mỗi phần được định nghĩa trong một file code riêng biệt. Khi biên dịch, tất cả các phần này sẽ được kết hợp lại thành một lớp duy nhất.
Cách sử dụng Partial Class có thể rất hữu ích trong một số tình huống như:
-
Tạo lớp có kích thước lớn: Khi một lớp trở nên quá lớn và phức tạp, việc chia thành nhiều phần sẽ giúp dễ quản lý mã nguồn và làm cho mã trở nên dễ đọc hơn.
-
Tạo lớp cho các kiểu dữ liệu được sinh ra tự động: Trong các tình huống như khi sử dụng các công cụ tạo mã tự động (ví dụ: Entity Framework), Partial Class có thể được sử dụng để tạo ra một phần của lớp và giữ cho các phần được sinh ra tự động và mã nguồn do người dùng cùng tồn tại trong cùng một lớp.
-
Phát triển đồng thời: Nhiều lập trình viên có thể làm việc trên cùng một lớp mà không cần phải lo lắng về việc xung đột giữa các thay đổi của họ.
partial class MyClass // MyClass_1.cs
{
public void Method1()
{
Console.WriteLine("Method 1");
}
}
partial class MyClass // MyClass_2.cs
{
public void Method_2()
{
Console.WriteLine("Method 2");
}
}
class Program
{
static void Main()
{
MyClass obj = new MyClass();
obj.Method_1();
obj.Method_2();
}
}
Các biến kiểu dynamic trong C # là gì?
Trong C#, dynamic
là một kiểu dữ liệu được sử dụng để khai báo biến mà giá trị và kiểu dữ liệu của nó có thể được xác định tại thời điểm chạy (runtime) thay vì tại thời điểm biên dịch. Biến kiểu dynamic
cho phép bạn làm việc với các đối tượng mà kiểu của chúng không được xác định trước hoặc có thể thay đổi trong quá trình chạy của ứng dụng.
Một số điểm chính về biến kiểu dynamic
:
-
Loại được xác định tại thời điểm chạy: Thay vì loại dữ liệu được xác định tại thời điểm biên dịch, biến kiểu dynamic
cho phép bạn làm việc với các đối tượng mà kiểu của chúng được xác định tại thời điểm chạy.
-
Tính linh hoạt cao: Biến kiểu dynamic
cho phép bạn thực hiện các phép toán, gọi các phương thức, truy cập các thuộc tính mà không cần phải biết kiểu dữ liệu chính xác của đối tượng.
-
Kiểm tra kiểu tại thời điểm chạy (Runtime type checking): Trong quá trình thực thi, kiểu của biến dynamic
được kiểm tra tại thời điểm chạy, điều này có thể dẫn đến các lỗi tại thời điểm chạy nếu sử dụng không đúng kiểu dữ liệu.
Ví dụ:
dynamic obj = "Hello";
Console.WriteLine(obj); // Output: Hello
obj = 10;
Console.WriteLine(obj); // Output: 10
obj = new { Name = "John", Age = 30 };
Console.WriteLine(obj.Name); // Output: John
Console.WriteLine(obj.Age); // Output: 30
Managed Code và Unmanaged Code là gì?
-
Managed Code:
- Managed Code là mã được thực thi trong một môi trường quản lý, thường là một môi trường runtime như Common Language Runtime (CLR) của .NET Framework.
- Trong môi trường quản lý, mã được quản lý bởi một máy ảo runtime (runtime environment), có nhiệm vụ quản lý bộ nhớ, kiểm soát truy cập đến tài nguyên hệ thống, và cung cấp các tính năng như thu gom rác (garbage collection), kiểm tra kiểu tại thời điểm chạy (runtime type checking), và quản lý chu trình sống của các đối tượng.
- Ngôn ngữ như C# và VB.NET thường tạo ra Managed Code khi biên dịch. Khi chạy, mã này được thực thi bởi một môi trường runtime như CLR.
-
Unmanaged Code:
- Unmanaged Code là mã được thực thi trong một môi trường không được quản lý bởi một máy ảo runtime.
- Trong môi trường không quản lý, lập trình viên phải tự quản lý bộ nhớ và tài nguyên hệ thống. Các ngôn ngữ như C và C++ thường tạo ra Unmanaged Code.
- Unmanaged Code có thể là mã máy (machine code) hoặc mã đã được biên dịch thành ngôn ngữ gần máy tính (ví dụ: mã assembly), và thường được sử dụng trong các ứng dụng cần hiệu suất cao và kiểm soát gần gũi với phần cứng.
Có mấy loại class trong C#
Trong C#, có 4 loại class chính:
- Class tham chiếu (Reference type): Đây là loại class phổ biến nhất. Khi bạn tạo một biến kiểu class tham chiếu, biến đó sẽ lưu trữ tham chiếu đến vị trí bộ nhớ của đối tượng. Khi bạn gán một biến class tham chiếu cho một biến khác, cả hai biến sẽ tham chiếu đến cùng một đối tượng.
- Class giá trị (Value type): Khi bạn tạo một biến kiểu class giá trị, biến đó sẽ lưu trữ một bản sao của dữ liệu của đối tượng. Khi bạn gán một biến class giá trị cho một biến khác, hai biến sẽ chứa hai bản sao độc lập của dữ liệu.
- Class tĩnh (Static class): Class tĩnh không thể tạo ra các đối tượng. Các thành viên của class tĩnh (bao gồm biến và phương thức) được truy cập trực tiếp trên tên class mà không cần tạo đối tượng.
- Class trừu tượng (Abstract class): Class trừu tượng không thể tạo ra các đối tượng. Class trừu tượng được sử dụng để định nghĩa các phương thức trừu tượng (không có thân) mà các lớp con phải triển khai.
Ngoài 4 loại class chính này, C# còn có một số loại class đặc biệt khác như:
- Class sealed: Class sealed không thể được kế thừa bởi các lớp khác.
- Class partial: Class partial cho phép bạn chia định nghĩa class thành nhiều phần được đặt trong nhiều file khác nhau.
- Class nested: Class nested là class được định nghĩa bên trong một class khác.
Có thể truyền tham số cho một phương thức bằng bao nhiêu cách?
Truyền tham số bằng giá trị: Truyền tham số bằng giá trị là cách thông thường nhất, trong đó giá trị của biến được chuyển đến phương thức. Bất kỳ thay đổi nào được thực hiện trên tham số trong phương thức sẽ không ảnh hưởng đến giá trị của biến gốc.
void MethodByValue(int x) { x += 10; }
Truyền tham số bằng tham chiếu: Truyền tham số bằng tham chiếu cho phép phương thức thay đổi giá trị của biến gốc bằng cách sử dụng từ khóa ref
hoặc out
.
void MethodByReference(ref int x) { x += 10; }
Truyền tham số bằng giá trị đã được gán mặc định (default value): Trong C#, bạn có thể định nghĩa các tham số với giá trị mặc định, điều này có nghĩa là bạn có thể gọi phương thức mà không cần truyền giá trị cho tham số đó.
void MethodWithDefaultValue(int x = 10) { // Do something }
Truyền tham số bằng mảng tham số (params): Tham số params cho phép bạn truyền một số lượng biến động của các tham số cùng một kiểu dữ liệu, và chúng sẽ được xử lý như một mảng trong phương thức.
void MethodWithParams(params int[] numbers) { // Do something with numbers }
Truyền tham số bằng phương thức (method parameters): Bạn cũng có thể truyền một phương thức như là một tham số cho một phương thức khác.
void MethodWithDelegate(Action method) { method(10); }