Tại sao sử dụng từ khóa params?


336

Tôi biết đây là một câu hỏi cơ bản, nhưng tôi không thể tìm thấy câu trả lời.

Tại sao sử dụng nó? nếu bạn viết một hàm hoặc một phương thức sử dụng nó, khi bạn loại bỏ nó, mã sẽ vẫn hoạt động hoàn hảo, 100% như không có nó. Ví dụ:

Với thông số:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

Không có thông số:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}

62
Bản thân mã của phương thức vẫn sẽ hoạt động hoàn hảo ... mã gọi có thể không ...
Jon Skeet

7
từ khóa params có nghĩa là các tham số TÙY CHỌN có thể được truyền hoặc không cho Phương thức. Một mảng với từ khóa params có nghĩa là bạn PHẢI truyền đối số mảng cho phương thức.
Ailayna Entarria

Ngôn ngữ Python thực hiện cùng một khái niệm rất ngọt ngào với *tham số tiền tố dấu hoa thị ( ) như được đề cập ở đây .
RBT

Câu trả lời:


485

Vớiparams bạn có thể gọi phương thức của bạn như thế này:

addTwoEach(1, 2, 3, 4, 5);

Không có params, bạn không thể.

Ngoài ra, bạn có thể gọi phương thức với một mảng là một tham số trong cả hai trường hợp :

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

Đó là, paramscho phép bạn sử dụng một phím tắt khi gọi phương thức.

Không liên quan, bạn có thể rút ngắn đáng kể phương pháp của mình:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}

17
@Ken: Bạn có thể cần nhập System.Linqkhông gian tên :)
Ranhiru Jude Cooray

49
Hoặc trả về args.Sum (i => i + 2);
sinhfromanegg

2
Tuy nhiên, tổng với một đại biểu không làm tăng độ phức tạp của mã được biên dịch, có khả năng có thể ít hiệu suất hơn. Không thực sự có liên quan trong tình huống cụ thể này, vì nó sẽ không dẫn đến bất kỳ sự đóng cửa nào, nhưng đáng để biết những gì nhà soạn nhạc thực sự làm để đưa ra lựa chọn tốt nhất.
Allen Clark Copeland Jr

2
Bạn cũng có thể sử dụng return args.Select(x => x + 2).Sum();
bbvg

1
Khi bạn thêm, paramsbạn tự khóa mình bằng cách thêm các đối số phương thức bổ sung mà không phá vỡ các cuộc gọi hoặc giải pháp phương thức của bạn.
Ông trẻ

93

Sử dụng paramscho phép bạn gọi hàm không có đối số. Không có params:

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

So sánh với params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

Nói chung, bạn có thể sử dụng tham số khi số lượng đối số có thể thay đổi từ 0 đến vô cùng và sử dụng một mảng khi số lượng đối số thay đổi từ 1 đến vô cùng.


13
thực sự là một mảng có thể trống new int[0]. hi vọng điêu nay co ich! :)
vidstige

22

Nó cho phép bạn thêm bao nhiêu tham số loại cơ sở trong cuộc gọi của bạn như bạn muốn.

addTwoEach(10, 2, 4, 6)

trong khi với dạng thứ hai, bạn phải sử dụng một mảng làm tham số

addTwoEach(new int[] {10,2,4,6})

17

Một mối nguy hiểm với paramsKeyword là, nếu sau khi các cuộc gọi đến Phương thức được mã hóa,

  1. ai đó vô tình / cố ý xóa một / nhiều tham số bắt buộc khỏi Chữ ký phương thức và
  2. một / nhiều tham số bắt buộc ngay trước paramsThông số trước khi thay đổi Chữ ký là Loại tương thích với paramsThông số,

các Cuộc gọi đó sẽ tiếp tục biên dịch với một / nhiều Biểu thức được dự định trước đó cho các Tham số bắt buộc được coi là paramsTham số tùy chọn . Tôi vừa gặp phải trường hợp xấu nhất có thể xảy ra: paramsThông số này thuộc loạiobject[] .

Điều này rất đáng chú ý vì các nhà phát triển đã quen với trình biên dịch vỗ cổ tay của họ với kịch bản phổ biến hơn nhiều, trong đó Tham số được xóa khỏi Phương thức với tất cả các Tham số bắt buộc (vì # Thông số dự kiến ​​sẽ thay đổi).

Đối với tôi, nó không đáng để đi tắt. (Type)[]không có paramssẽ hoạt động với 0 đến vô hạn # Thông số mà không cần Ghi đè. Trường hợp xấu nhất là bạn sẽ phải thêm một , new (Type) [] {}Cuộc gọi khi không áp dụng.

Btw, imho, cách an toàn nhất (và dễ đọc nhất) là:

  1. vượt qua Thông số được đặt tên (mà bây giờ chúng ta có thể thực hiện ngay cả trong C # ~ 2 thập kỷ sau khi chúng ta có thể trong VB; P) (vì:

    1.1. đó là cách duy nhất đảm bảo ngăn ngừa các giá trị ngoài ý muốn được chuyển đến Tham số sau khi thứ tự Thông số, Loại tương thích và / hoặc thay đổi số sau khi Cuộc gọi được mã hóa,

    1.2. nó làm giảm các cơ hội đó sau khi thay đổi ý nghĩa Thông số, bởi vì tên định danh mới có khả năng phản ánh ý nghĩa mới nằm ngay bên cạnh giá trị được truyền cho nó,

    1.3. nó tránh việc phải đếm các dấu phẩy và nhảy qua lại từ Call to Signature để xem Biểu thức nào được truyền cho Thông số nào và

    1.3.1. Nhân tiện, lý do này thôi cũng rất nhiều (về việc tránh vi phạm thường xuyên bị lỗi Nguyên tắc DRY chỉ để đọc mã không đề cập đến cũng sửa đổi nó), nhưng lý do này có thể quan trọng hơn theo cấp số nhân nếu có một / Nhiều biểu thức được truyền đi mà bản thân chúng có chứa dấu phẩy, tức là các tham chiếu mảng nhiều chiều hoặc các lệnh gọi hàm đa tham số. Trong trường hợp đó, bạn thậm chí không thể sử dụng (mà ngay cả khi bạn có thể, vẫn sẽ thêm một bước bổ sung cho mỗi Thông số trên mỗi Cuộc gọi phương thức), Tìm tất cả các lần xuất hiện trong tính năng Lựa chọn trong trình chỉnh sửa của bạn để tự động đếm dấu phẩy cho bạn.

    1.4. nếu bạn phải sử dụng Thông số tùy chọn ( paramshoặc không), nó cho phép bạn tìm kiếm Cuộc gọi trong đó Thông số tùy chọn cụ thể được truyền (và do đó, rất có thể không hoặc ít nhất có khả năng không phải là Giá trị mặc định),

(LƯU Ý: Lý do 1.2. Và 1.3. Có thể giảm bớt và giảm khả năng xảy ra lỗi ngay cả khi mã hóa các Cuộc gọi ban đầu không được đề cập khi các Cuộc gọi phải được đọc và / hoặc thay đổi.))

  1. làm như vậy MỘT - PARAMETER - PER - LINE để dễ đọc hơn (vì:

    2.1. nó ít lộn xộn hơn, và

    2.2. nó tránh được việc phải cuộn sang phải và quay lại bên trái (và phải làm như vậy PER - LINE, vì hầu hết người phàm không thể đọc phần bên trái của nhiều dòng, cuộn sang phải và đọc phần bên phải)).

    2.3. nó phù hợp với "Thực tiễn tốt nhất" mà chúng tôi đã phát triển thành Báo cáo chuyển nhượng, bởi vì mọi Thông số được truyền về bản chất là Tuyên bố chuyển nhượng (gán Giá trị hoặc Tham chiếu cho Biến cục bộ). Giống như những người theo "Thực tiễn tốt nhất" mới nhất trong Phong cách mã hóa sẽ không mơ ước được mã hóa nhiều Tuyên bố chuyển nhượng trên mỗi dòng, có lẽ chúng ta không nên (và sẽ không một lần "Thực hành tốt nhất" bắt kịp "thiên tài" của tôi; P ) làm như vậy khi truyền tham số.

GHI CHÚ :

  1. Chuyển qua các biến có tên phản ánh các tham số 'không giúp ích gì khi:

    1.1. bạn đang chuyển qua các hằng số theo nghĩa đen (nghĩa là 0/1 đơn giản, sai / đúng hoặc không có giá trị ngay cả "" Thực tiễn tốt nhất "" có thể không yêu cầu bạn sử dụng Hằng số được đặt tên và mục đích của chúng không thể dễ dàng được suy ra từ tên Phương thức ),

    1.2. Phương thức ở mức thấp hơn / chung chung hơn đáng kể so với Người gọi mà bạn không muốn / có thể đặt tên Biến của mình giống / tương tự với Tham số (hoặc ngược lại), hoặc

    1.3. bạn đang đặt hàng lại / thay thế Tham số trong Chữ ký có thể dẫn đến các Cuộc gọi trước vẫn đang Biên dịch vì Các loại xảy ra vẫn tương thích.

  2. Có tính năng tự động ngắt như VS chỉ giúp loại bỏ MỘT (# 2.2) trong số 8 lý do tôi đưa ra ở trên. Trước cuối năm 2015, nó KHÔNG tự động thụt lề (!?! Thực sự, MS?!?) Làm tăng mức độ nghiêm trọng của lý do # 2.1.

VS nên có một tùy chọn tạo đoạn trích cuộc gọi phương thức với tham số được đặt tên (một khóa trên mỗi dòng; P) và tùy chọn trình biên dịch yêu cầu tham số được đặt tên (tương tự như khái niệm với tùy chọn trong VB, btw, yêu cầu của nó là hoàn toàn từng nghĩ không kém phần thái quá nhưng hiện tại được yêu cầu hoàn toàn bởi "'Thực tiễn tốt nhất'"). Trong thực tế, "trở lại trong tôingày ";), vào năm 1991 chỉ vài tháng trong sự nghiệp của tôi, ngay cả trước khi tôi đang sử dụng (hoặc thậm chí đã nhìn thấy) một ngôn ngữ với Thông số được đặt tên, tôi đã chống lại /" chỉ là bạn có thể, không có nghĩa là bạn nên " / đừng mù quáng "cắt các đầu của rang" đủ để mô phỏng nó (sử dụng các bình luận nội tuyến) mà không thấy ai làm như vậy. Không phải sử dụng Tham số được đặt tên (cũng như các cú pháp khác để lưu "'quý'" tổ hợp phím mã nguồn) là một di tích của kỷ nguyên Punch Card khi hầu hết các cú pháp này bắt đầu. Không có lý do nào cho điều đó với phần cứng và IDE hiện đại và phần mềm phức tạp hơn nhiều trong đó khả năng đọc rất nhiều, Nhiều, NHIỀUquan trọng hơn. "Mã được đọc thường xuyên hơn nhiều so với được viết". Miễn là bạn không sao chép mã không được cập nhật tự động, mỗi lần nhấn phím được lưu có thể sẽ có giá cao hơn theo cấp số nhân khi ai đó (ngay cả chính bạn) đang cố đọc nó sau này.


2
Tôi không hiểu Tại sao bạn không thể thực thi rằng có ít nhất một? Ngay cả khi không có thông số, không có gì ngăn bạn vượt qua nullhoặc new object[0]làm đối số.
Casey

Có lẽ nó quá nguy hiểm khi từng có các tham số bắt buộc trước tùy chọn (trong trường hợp một hoặc nhiều trong số các tham số bắt buộc đó bị xóa sau khi các cuộc gọi được mã hóa). Đó có thể là lý do tại sao tôi chưa bao giờ thấy các tham số bắt buộc trước tham số tùy chọn trong mã mẫu trong bất kỳ tài liệu nào về các tham số tùy chọn. Btw, imho, cách an toàn nhất (và dễ đọc nhất) là chuyển qua các tham số đã đặt tên (mà bây giờ chúng ta có thể thực hiện ngay cả trong C # ~ 2 thập kỷ sau khi có thể trong VB). VS nên có một tùy chọn tạo các đoạn gọi phương thức với các tham số được đặt tên (và thực hiện 1 tham số trên mỗi dòng).
Tom

Tôi thực sự không chắc ý của bạn là gì. Cách duy nhất bạn có thể có các tham số bắt buộc và các tham số tùy chọn là chỉ định tất cả các tham số bắt buộc trước.
Casey

1
Ví dụ. Tôi tuyên bố myMethodvoid myMethod(int requiredInt, params int[] optionalInts). I / mã người khác một / nhiều cuộc gọi, tức là myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Tôi thay đổi myMethodđể được void myMethod(params int[] optionalInts). Tất cả các cuộc gọi đó vẫn sẽ biên dịch mà không có lỗi mặc dù các tham số đầu tiên của chúng ("1") rõ ràng không có ý định được chuyển thành phần tử thứ nhất của optionalIntsThông số.
Tom

Oh. Chà, OK, trong trường hợp cụ thể đó có thể là điều không nên. Tôi không nghĩ có bất kỳ lý do nào để tránh nó nếu bạn cần một chuỗi và 0 đến nhiều số nguyên hoặc bất cứ điều gì.
Casey

10

Không cần tạo phương thức quá tải, chỉ cần sử dụng một phương thức duy nhất với thông số như dưới đây

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);

Cảm ơn cho đầu vào của bạn nhưng tôi không nghĩ những loại quá tải này sẽ là một giải pháp vì dù có paramshay không, chúng tôi sẽ chỉ chuyển một loại bộ sưu tập để bao gồm bất kỳ số lượng bộ sưu tập nào.
MasterMastic

Bạn đúng ở một mức độ nào đó nhưng điều làm cho nó mát mẻ là sự quá tải không có tham số đầu vào, ví dụ int sum1 = addTwoEach ();
Electricbah

5

params cũng cho phép bạn gọi phương thức với một đối số duy nhất.

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

tức là Foo(1);thay vì Foo(new int[] { 1 });. Có thể hữu ích cho tốc ký trong các tình huống mà bạn có thể cần phải vượt qua trong một giá trị thay vì toàn bộ một mảng. Nó vẫn được xử lý theo cách tương tự trong phương thức, nhưng đưa ra một số kẹo để gọi theo cách này.


5

Thêm chính từ khóa params cho thấy bạn có thể truyền nhiều số tham số trong khi gọi phương thức đó là không thể nếu không sử dụng nó. Để cụ thể hơn:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

Khi bạn sẽ gọi phương thức trên, bạn có thể gọi nó bằng bất kỳ cách nào sau đây:

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

Nhưng khi bạn sẽ loại bỏ từ khóa params, cách thứ ba trong số các cách đã cho ở trên sẽ hoạt động tốt. Đối với trường hợp thứ 1 và thứ 2, bạn sẽ gặp lỗi.


0

Một điều quan trọng hơn cần được làm nổi bật. Nó tốt hơn để sử dụng paramsvì nó tốt hơn cho hiệu suất. Khi bạn gọi một phương thức với paramsđối số và truyền cho nó không có gì:

public void ExampleMethod(params string[] args)
{
// do some stuff
}

gọi:

ExampleMethod();

Sau đó, một phiên bản mới của .Net Framework thực hiện việc này (từ .Net Framework 4.6):

ExampleMethod(Array.Empty<string>());

Array.EmptyĐối tượng này có thể được sử dụng lại bởi khung sau này, do đó không cần phải phân bổ dự phòng. Những phân bổ này sẽ xảy ra khi bạn gọi phương thức này như thế này:

 ExampleMethod(new string[] {});

0

Nghe có vẻ ngu ngốc, nhưng Params không cho phép mảng đa chiều. Trong khi đó bạn có thể truyền một mảng nhiều chiều cho một hàm.


0

Một vi dụ khac

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

var items = Tokenize(product.Name, product.FullName, product.Xyz)
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.