Phương pháp c # với tham số không giới hạn hoặc phương thức với một mảng hoặc danh sách?


21

Gần đây tôi đã biết rằng bạn có thể tạo một số phương thức với các tham số không giới hạn, ví dụ:

SomeMethod(params int[] numbers);

Nhưng câu hỏi của tôi là, sự khác biệt giữa điều đó và chỉ tạo ra một phương thức nhận danh sách hoặc một mảng là gì?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

có lẽ nó có một số tác động trong hiệu suất? Tôi không hoàn toàn hiểu hoặc nhìn theo cách bạn thích cái có thông số không giới hạn.

Một tìm kiếm nhanh trên google không giúp được gì, tôi hy vọng bạn có thể giúp tôi.



Ngoài câu trả lời của tôi dưới đây, paramscũng yêu cầu loại đối số là một mảng. Nếu bạn cần tiêu thụ một bộ sưu tập không phải là một mảng, thì có thể có ý nghĩa hơn để cung cấp một paramsđối số thay thế.
Digitlworld

1
@digitlworld: Nếu chủ đề này làm bạn quan tâm, hãy xem github.com/dotnet/csharplang/issues/179 để thảo luận.
Eric Lippert

Nhìn vào chữ ký và / hoặc thực hiện Console.Write.
3Dave

Câu trả lời:


27

sự khác biệt giữa điều đó và chỉ tạo ra một phương thức nhận danh sách hoặc mảng?

Sự khác biệt giữa

void M(params int[] x)

void N(int[] x)

M có thể được gọi như thế này không:

M(1, 2, 3)

hoặc như thế này:

M(new int[] { 1, 2, 3 });

nhưng N chỉ có thể được gọi theo cách thứ hai , không phải cách thứ nhất.

có lẽ nó có một số tác động trong hiệu suất?

Tác động đến hiệu suất là cho dù bạn gọi Mtheo cách thứ nhất hay cách thứ hai, dù bằng cách nào bạn cũng có được một mảng được tạo. Tạo một mảng có tác động hiệu suất vì nó tốn cả thời gian và bộ nhớ. Hãy nhớ rằng các tác động hiệu suất nên được đo lường dựa trên các mục tiêu hiệu suất; không chắc rằng chi phí tạo ra một mảng bổ sung là yếu tố gating là sự khác biệt giữa thành công và thất bại trên thị trường.

Tôi không hoàn toàn hiểu hoặc nhìn theo cách bạn thích cái có thông số không giới hạn.

Nó hoàn toàn và hoàn toàn thuận tiện cho tác giả của mã đang gọi phương thức; nó chỉ đơn giản là ngắn hơn và dễ viết hơn

M(1, 2, 3);

thay vì viết

M(new int[] { 1, 2, 3 });

Nó chỉ lưu một vài tổ hợp phím bên phía người gọi. Đó là tất cả.

Một vài câu hỏi bạn không hỏi nhưng có lẽ muốn biết câu trả lời:

Tính năng này được gọi là gì?

Các phương thức cho phép một số lượng đối số khác nhau được truyền vào phía người gọi được gọi là matrixdic . Các phương thức Params là cách C # thực hiện các phương thức matrixdic.

Làm thế nào để giải quyết quá tải làm việc với một phương pháp matrixdic?

Khi gặp vấn đề về độ phân giải quá tải, C # sẽ xem xét cả hai dạng "bình thường" và "mở rộng" và dạng "bình thường" luôn thắng nếu cả hai đều được áp dụng. Ví dụ, hãy xem xét điều này:

void P(params object[] x){}

và chúng tôi có một cuộc gọi

P(null);

Có hai khả năng áp dụng. Ở dạng "bình thường", chúng tôi gọi Pvà chuyển một tham chiếu null cho mảng. Ở dạng "mở rộng", chúng tôi gọi P(new object[] { null }). Trong trường hợp này, hình thức bình thường chiến thắng. Nếu chúng tôi có một cuộc gọi P(null, null)thì hình thức bình thường là không thể áp dụng và hình thức mở rộng sẽ thắng theo mặc định.

Thách thức : Giả sử chúng ta có var s = new[] { "hello" };và một cuộc gọi P(s);. Mô tả những gì xảy ra tại trang web cuộc gọi và tại sao. Bạn có thể ngạc nhiên!

Thử thách : Giả sử chúng ta có cả void P(object x){}void P(params object[] x){}. Làm gì P(null), và tại sao?

Thử thách : Giả sử chúng ta có cả void M(string x){}void M(params string[] x){}. Làm gì M(null), và tại sao? Điều này khác với trường hợp trước như thế nào?


Ch2: P(null)vs. P((object)null)vs. P((object[])null)- tôi có thể tìm lời giải thích cho sự khác biệt này ở đâu? Cảm giác giống như nullcó một số loại đặc biệt , chuyển đổi thành mảng chứ không phải là đối tượng ( P(null)), nhưng tìm thấy chuyển đổi thành chuỗi hoặc mảng chuỗi mơ hồ ( M(null)) ... mà cảm thấy rất lạ, sẽ mong đợi nó sẽ mơ hồ trong cả hai trường hợp hoặc chọn phiên bản đơn arg (mà nó không). Nhưng tôi đoán nó liên quan nhiều hơn đến paramsviệc chung chung , như tham chiếu phổ quát ( &&) trong các mẫu C ++ và do đó phù hợp hơn (trong Ch2, không phải trong Ch3).
firda

@firda: Bạn có thể tìm thấy lời giải thích trong đặc tả C #. Lý do cho sự kỳ lạ là: null có thể chuyển đổi thành đối tượng, chuỗi, đối tượng [] và chuỗi []. Vì vậy, câu hỏi đặt ra cho độ phân giải quá tải là: chuyển đổi nào là tốt nhất ? Quy tắc kiểm soát trong trường hợp này là cụ thể là tốt hơn so với chung . Làm thế nào để chúng ta biết loại nào là cụ thể hơn? Quy tắc là: tất cả hươu cao cổ đều là động vật nhưng không phải tất cả động vật đều là hươu cao cổ, do đó, hươu cao cổ đặc biệt hơn động vật. Tất cả object[]object, nhưng không phải tất cả objectobject[], do đó object[]là cụ thể hơn.
Eric Lippert

@firda: Bây giờ bạn đã biết tại sao các chuỗi không rõ ràng. Nó không đúng là "tất cả string[]string." Trong thực tế NO string[]stringvà NO stringstring[], vì vậy stringkhông phải là cụ thể hơn hoặc tổng quát hơn string[], và chúng tôi nhận được một lỗi mơ hồ khi được hỏi để lựa chọn.
Eric Lippert

Tất cả object[]object... object[]cụ thể hơn vì vậy P(null)đi cho mảng cụ thể hơn, thx, đó là những gì tôi đã thiếu. Tìm thấy một số quy tắc ở đây: docs.microsoft.com/en-us/dotnet/csharp/lingu-reference/ trộm
firda

5

Chỉ cần làm một nguyên mẫu nhỏ. Câu trả lời dường như paramschỉ đơn giản là cú pháp đường để truyền trong một mảng. Đó không thực sự là một bất ngờ. Tôi đã tạo hai phiên bản của cùng một phương thức, trong đó điểm khác biệt duy nhất là từ khóa "params". IL được tạo cho cả hai giống hệt nhau, ngoại trừ việc a System.ParamArrayAttributeđược áp dụng cho paramsphiên bản.

Hơn nữa, IL được tạo tại trang web cuộc gọi cũng giống như giữa tôi gọi phương thức với một khai báo thủ công new int[]và gọi phương thức chỉ bằng cách sử dụng các paramsđối số.

Vì vậy, câu trả lời dường như là "sự tiện lợi". Dường như không có sự khác biệt nào về hiệu suất. Bạn cũng có thể gọi một paramshàm với một mảng thay vào đó, vì vậy điều đó cũng không đáng ngạc nhiên lắm. Nếu người tiêu dùng phương thức của bạn gọi nó bằng bất kỳ số lượng tham số nào (ví dụ someMethod(1, 2, 3)) sẽ dễ dàng hơn là luôn phải tạo một bộ sưu tập trước (ví dụ someMethod(new List<int>() { 1, 2, 3 } )).


4

Tính năng của các tham số không giới hạn cung cấp các lợi ích sau trong nhiều tình huống:

  1. Khớp nối lỏng lẻo
  2. Tái sử dụng nâng cao
  3. Hiệu suất tổng thể tốt hơn của ứng dụng

Dưới đây là một ví dụ trong đó tùy chọn tham số không giới hạn là một lựa chọn tuyệt vời

Hãy xem xét rằng một ứng dụng để gửi email cần phải được xây dựng.

Hàm gửi email phải có khả năng xử lý một hoặc nhiều giá trị cho các trường 'Tới', 'CC' và 'BCC'.

Nếu các loại tham số được cố định là mảng hoặc danh sách cho tất cả các trường (To, CC, BCC), thì chức năng gọi sẽ buộc phải xử lý tất cả sự phức tạp của việc xác định 3 mảng hoặc danh sách để gọi chức năng người gửi email .

Ngay cả khi người gọi muốn gửi email đến một địa chỉ, chức năng người gửi email sẽ buộc người gọi xác định và gửi 3 mảng khác nhau làm tham số.

Nếu chức năng người gửi email có cách tiếp cận params không giới hạn, thì chức năng người gọi không cần phải giải quyết tất cả sự phức tạp.

Cách tiếp cận tham số không giới hạn góp phần cải thiện thời gian chạy của ứng dụng tốt hơn bằng cách tránh việc tạo các mảng hoặc danh sách bất cứ khi nào không cần thiết.


2

Từ góc độ không hiệu năng , phong cách, paramstừ khóa thực sự tốt đẹp khi bạn muốn gửi danh sách các tham số tùy chọn.

Cá nhân, tôi sẽ sử dụng paramskhi mã của tôi là một cái gì đó như

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

Phần thú vị về điều này là tôi có thể sử dụng phương pháp này ở mọi nơi mà không phải tạo một mảng hoặc danh sách mỗi lần.

Tôi sẽ sử dụng arrayhoặclist khi tôi biết tôi sẽ luôn truyền cho hàm này một tập hợp dữ liệu đã được đặt cùng nhau như

List<User> users = GetUsersFromDB();
SomeMethod(users);

Tôi thấy lợi ích của paramssự linh hoạt mà nó thêm vào. Nó có thể là một tác động tương đối nhỏ đến mã của bạn, nhưng nó vẫn là một công cụ tốt để có.


1

Quy ước gọi là khác nhau. Ví dụ ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

Trong trường hợp đầu tiên, bạn có thể truyền nhiều int như các tham số riêng cho phương thức. Trong lần thứ hai, bạn sẽ cần khởi tạo một danh sách và vượt qua nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.