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

Khi nào sử dụng IEnumerable thay vì List, và chúng hoạt động như thế nào?


IEnumerable mô tả hành vi, còn List là một triển khai của hành vi đó. Khi bạn sử dụng IEnumerable, bạn cho phép trình biên dịch trì hoãn hoạt động cho đến sau này, có thể sẽ tối ưu hóa trong quá trình thực hiện. Nếu bạn sử dụng ToList(), bạn buộc trình biên dịch phải thay đổi kết quả ngay lập tức.

Bất cứ khi nào bạn "xếp chồng" các biểu thức LINQ, bạn nên sử dụng IEnumerable, bởi vì chỉ xác định hành vi cho phép LINQ có cơ hội trì hoãn việc đánh giá và có thể tối ưu hóa chương trình.

Sự khác biệt giữa Func< string, string > và delegate?


Vấn đề:

Func<string, string> convertMethod = lambda; // A
public delegate string convertMethod(string value); // B

Cả hai đều là delegate? Chúng có gì khác biệt không?

Giải pháp: đầu tiên là khai báo một biến delegate và gán giá trị cho nó, thứ hai là chỉ định nghĩa một kiểu delegate. Cả Func<string,string>delegate string convertMethod(string) sẽ có khả năng giữ các định nghĩa phương thức giống nhau cho dù chúng là phương thức, phương thức anonymous hay biểu thức lambda.

public static class Program
{
// you can define your own delegate for a nice meaningful name, but the
// generic delegates (Func, Action, Predicate) are all defined already
public delegate string ConvertedMethod(string value);

public static void Main()
{
// both work fine for taking methods, lambdas, etc.
Func<string, string> convertedMethod = s => s + ", Hello!";
ConvertedMethod convertedMethod2 = s => s + ", Hello!";
}
}

Giải thích sự khác biệt giữa Select và Where?


Vấn đề:

ContextSet().Select(x=> x.FirstName == "John") // A
ContextSet().Where(x=> x.FirstName == "John") // B

Khi nào chúng ta nên sử dụng .Select thay vì .Where?

Giải pháp: select là một phép chiếu, vì vậy những gì bạn nhận được là biểu thức x => x.FirstName == "John" được ước tính cho mỗi phần tử trong ContextSet() trên máy chủ, tức là rất nhiều giá trị true/false (cùng số lượng với danh sách ban đầu của bạn). Nếu bạn nhìn vào vùng chọn sẽ trả về một cái gì đó giống như IEnumerable<bool> (vì kiểu của x => x.FirstName == "John" là bool). Sử dụng Select khi bạn muốn giữ tất cả các kết quả, nhưng thay đổi kiểu của chúng (chiếu chúng). Where lọc các kết quả, trả về một kiểu dữ liệu ban đầu (không có phép chiếu). Sử dụng Where khi bạn muốn lọc kết quả của mình, giữ nguyên kiểu ban đầu.

Giải thích Short-Circuit Evaluation trong C#?


Short-circuit evaluation là một phương pháp phức tạp để đánh giá các toán tử logic ANDOR. Trong phương pháp này, toàn bộ biểu thức có thể được đánh giá là true hoặc false mà không cần đánh giá tất cả các biểu thức con. Thông thường Short-circuit chỉ đánh giá phía bên phải nếu phía bên trái chưa xác định kết quả.

if(myObj != null && myObj.SomeString != null)

Mặc dù C# (và một số ngôn ngữ .NET khác) hoạt động theo cách này, nhưng nó là một thuộc tính của ngôn ngữ, không phải của CLR.

Cách sử dụng nào là tốt nhất khi sử dụng các đối tượng Lazy?


Bạn thường sử dụng nó khi bạn muốn khởi tạo một thứ gì đó ở lần đầu tiên nó được sử dụng. Điều này chỉ làm chúng ta tốn chi phí khi tạo ra nó thay vì luôn luôn phát sinh chi phí. Thông thường điều này được ưu tiên sử dụng khi đối tượng có thể hoặc không thể được sử dụng và chi phí khởi taọ nó là không nhỏ.

Ví dụ: Lazy<T> giúp việc triển khai các singleton lazythread-safe dễ dàng:

public sealed class Singleton
{
   // Because Singleton's constructor is private, we must explicitly
   // give the Lazy<Singleton> a delegate for creating the Singleton.
   static readonly Lazy<Singleton> instanceHolder =
       new Lazy<Singleton>(() => new Singleton());

   Singleton()
   {
      // Explicit private constructor to prevent default public constructor.
      ...
   }

   public static Singleton Instance => instanceHolder.Value;
}

Điều gì xảy ra khi chúng ta Box hoặc Unbox các kiểu Nullable?


  • Khi một kiểu nullable được box (đóng gói), Common Language Runtime (CLR) sẽ tự động chọn giá trị cơ bản của đối tượng Nullable, không phải chính đối tượng Nullable. Đó là, nếu thuộc tính HasValuetrue, nội dung của thuộc tính Value được box. Nếu thuộc tính HasValuefalse, null được box.
  • Khi giá trị cơ bản của kiểu nullable được unbox (tiến trình ngược lại của box), CLR sẽ tạo ra một cấu trúc Nullable mới được khởi tạo theo giá trị cơ bản đó.

Bạn hãy giải thích sự khác biệt giữa interface, abstract class, sealed class, static class và partial class trong C#?


  • abstract class: nên được sử dụng khi có một mối quan hệ IS-A và không có instance nào được phép tạo từ abstract class đó. Ví dụ: một Animal là một lớp cơ sở abstract, nơi các loài động vật cụ thể có thể được kế thừa, ví dụ là Horse, Pig, v.v. Bằng cách tạo ra lớp abstract Animal, chúng ta không được phép tạo ra một instance của Animal.
  • interface: một interface nên được sử dụng để thực hiện chức năng trong một lớp. Giả sử chúng ta muốn một con ngựa có thể Jump, một IJumping interface có thể được tạo ra. Bằng cách thêm interface này vào Horse, tất cả các phương thức trong IJumping sẽ được triển khai. Trong riêng IJumping, chỉ có các khai báo (ví dụ: StartJump và EndJump), trong Horse, việc triển khai hai phương thức này phải được thêm vào.
  • sealed class: bằng cách làm niêm phong Horse, không lớp nào có thể kế thừa từ nó, ví dụ: thực hiện cho các lớp như Pony hoặc Workhorse mà bạn muốn được kế thừa từ Horse.
  • static class: chủ yếu được sử dụng cho các chức năng 'tiện ích'. Giả sử bạn cần một số phương thức để tính toán trung bình của một số được sử dụng trong lớp Horse, nhưng bạn không muốn đặt nó trong Horse vì nó không liên quan và nó cũng không liên quan đến động vật, bạn có thể tạo một lớp để loại phương thức này vào trong. Bạn không cần một instance của một lớp tiện ích như vậy.
  • partial class: một partial class không gì khác hơn là phân tách tệp của một lớp thành nhiều tệp nhỏ hơn. Một lý do để làm điều này có thể là để chia sẻ một phần của mã nguồn cho người khác. Nếu lý do là tệp quá lớn, hãy nghĩ về việc chia lớp đó thành các lớp khác nhỏ hơn trước.

Làm thế nào để giải quyết vấn đề Circular Reference?


Vấn đề: làm thế nào để bạn giải quyết các vấn đề circular reference, ví dụ như lớp A có lớp B là một trong những thuộc tính của nó, trong khi lớp B có lớp A là một trong những thuộc tính của nó?

Giải pháp: đầu tiên tôi sẽ nói bạn cần phải suy nghĩ lại về thiết kế của bạn. Circular reference như bạn mô tả thường là một lỗ hổng thiết kế. Trong hầu hết các trường hợp, khi tôi phải có hai thứ tham chiếu lẫn nhau, tôi đã tạo một interface để xóa circular reference này. Ví dụ:

TRƯỚC

public class Foo
{
   Bar myBar;
}

public class Bar
{
   Foo myFoo;
}

Biểu đồ phụ thuộc:

image

Foo phụ thuộc vào Bar, nhưng Bar cũng phụ thuộc vào Foo. Nếu chúng nằm trong các assembly riêng biệt, bạn sẽ gặp vấn đề về việc build code (ví dụ như Build Solution trong Visual Studio), đặc biệt nếu bạn thực hiện việc Clean Solution -> Build Solution.

SAU

public interface IBar
{}

public class Foo
{
IBar myBar;
}

public class Bar : IBar
{
Foo myFoo;
}

Biểu đồ phụ thuộc:

image

Cả Foo và Bar đều phụ thuộc vào IBar. Không có circular dependency, và nếu IBar được đặt trong assembly của chính nó, Foo và Bar đang trong các assembly riêng biệt thì cũng không có vấn đề gì.

Sự khác biệt giữa IEnumerable và IQueryable là gì?


  • IEnumerable<> interface chỉ ra rằng một cái gì đó có thể được liệt kê qua toàn bộ - nói cách khác, bạn có thể thực hiện một vòng lặp foreach trên đó.
  • IQueryable<> interface chỉ ra rằng thứ gì đó có một số loại nhà cung cấp truy vấn hỗ trợ có khả năng xem các biểu thức được cấp cho nó, và dịch chúng thành một số loại truy vấn.
  • IQueryable có hai thuộc tính cụ thể mà IEnumerable không có — một thuộc tính trỏ đến trình cung cấp truy vấn (ví dụ: trình cung cấp LINQ to SQL) và một thuộc tính khác trỏ đến biểu thức truy vấn đại diện cho đối tượng IQueryable dưới dạng cây cú pháp trừu tượng có thể duyệt qua runtime mà nhà cung cấp truy vấn đó có thể hiểu được (đối với hầu hết các trường hợp, bạn không thể đưa một biểu thức LINQ to SQL tới nhà cung cấp LINQ to Entities mà không có exception nào được thảy ra).

Khái niệm Deep Copy và Shallow Copy trong C# là gì?


  • Shallow Copy: tạo một đối tượng mới và sau đó sao chép các thuộc tính kiểu giá trị của đối tượng được sao chép sang đối tượng mới. Nhưng khi thuộc tính là kiểu tham chiếu, thì chỉ có tham chiếu là được sao chép chứ không phải bản thân đối tượng được thuộc tính đó tham chiếu đến. Do đó thuộc tính kiểu tham chiếu của bản gốc và bản sao đều tham chiếu đến cùng một đối tượng. Khái niệm này sẽ rõ ràng hơn khi bạn nhìn thấy sơ đồ của Shallow Copy.

image

  • Deep Copy: nó là một quá trình tạo một đối tượng mới và sau đó sao chép các thuộc tính của đối tượng được sao chép sang đối tượng mới để tạo một bản sao hoàn chỉnh của các kiểu tham chiếu bên trong, vì điều này, chúng ta cần cấu hình đối tượng được trả về bằng phương thức MemberwiseClone(). Nếu thuộc tính được sao chép là một kiểu giá trị, thì một bản sao từng bit của thuộc tính đó sẽ được thực hiện. Nếu thuộc tính được sao chép là một kiểu tham chiếu, thì đối tượng được thuộc tính đó tham chiếu đến sẽ được sao chép.

Sự khác biệt giữa System.Array.CopyTo() và System.Array.Clone() là gì?


  • Array.Clone() tạo một bản sao của mảng ban đầu. Nó trả về một mảng có độ dài bằng với mảng ban đầu. Phương thức Array.Clone() không yêu cầu mảng đích tồn tại sẵn vì nó tự tạo ra một mảng mới.
  • Array.CopyTo() sao chép các phần tử từ mảng ban đầu sang mảng đích bắt đầu từ chỉ số được chỉ định. Lưu ý rằng, việc này sẽ thêm các phần tử được chỉ định sao chép từ mảng ban đầu vào mảng đích.
  • Array.CopyTo() yêu cầu một mảng đích đã tồn tại trước đó và nó phải có khả năng giữ tất cả các phần tử được chỉ định sao chép từ mảng ban đầu.
  • Array.Clone()Array.CopyTo() đều thực hiện một shadow copy.

Multicast Delegate trong C# là gì?


Delegate chỉ có thể gọi một tham chiếu phương thức đã được đóng gói vào delegate. Một số delegate có thể giữ và gọi nhiều phương thức. Delegate như vậy được gọi là Multicast Delegate. Multicast Delegate (còn được gọi là Combinable Delegate) phải đáp ứng các điều kiện sau:

Kiểu trả về của Multicast Delegate phải là void. Không có tham số nào của Multicast Delegate có thể được khai báo dưới dạng tham số đầu ra bằng cách sử dụng từ khóa out. Instance của Multicast Delegate được tạo bằng cách kết hợp hai delegate, danh sách lệnh gọi được hình thành bằng cách nối danh sách lệnh gọi của hai toán hạng của thao tác cộng (+). Các delegate được gọi theo thứ tự được thêm vào. Trên thực tế, tất cả các delegate trong C# là Multicast Delegate, ngay cả khi chúng chỉ có một phương thức duy nhất. Ngay cả các hàm anonymouslambdas cũng là Multicast Delegate mặc dù theo định nghĩa, chúng chỉ có một mục tiêu duy nhất.

public partial class MainPage : PhoneApplicationPage
{
   public delegate void MyDelegate(int a, int b);
   // Constructor
   public MainPage()
   {
       InitializeComponent();

       // Multicast delegate
       MyDelegate myDel = new MyDelegate(AddNumbers);
       myDel += new MyDelegate(MultiplyNumbers);
       myDel(10, 20);
    }

    public void AddNumbers(int x, int y)
    {
       int sum = x + y;
       MessageBox.Show(sum.ToString());
    }

    public void MultiplyNumbers(int x, int y)
    {
        int mul = x * y;
        MessageBox.Show(mul.ToString());
    }
}
Avatar Techmely Team
VIẾT BỞI

Techmely Team