Đối số ra / ref tùy chọn C # 4.0


212

C # 4.0 có cho phép tùy chọn outhoặc refđối số không?


2
Weell, C ++ thực sự có chúng cho các tham số "out" - bạn có thể có một đối số địa chỉ được khởi tạo thành null và khá phổ biến để viết mã thư viện sẽ chỉ tạo ra cấu trúc trả về như vậy nếu con trỏ không null. Đó là một thành ngữ quay trở lại sử dụng null cho "đối số tùy chọn" trong API C.
Andy Dent

53
@Ed và mọi người: tại sao điều này sẽ vô nghĩa? Nếu một hàm "trả về" một giá trị thông qua "out", tôi không muốn bị buộc phải chấp nhận nó. Bây giờ tôi biết rằng vì lý do kỹ thuật, trình biên dịch vẫn phải truyền một cái gì đó vào, nhưng không có lý do gì mà nó không thể tạo ra một hình nộm cục bộ cho tôi sau lưng tôi.
Roman Starkov

5
Có lẽ nó không có ý nghĩa từ quan điểm về cách mọi thứ được thực hiện hoặc những gì một tham số tùy chọn thực sự là gì. Nhưng như romkyns đã nói, sẽ thật tuyệt khi có "đối số tùy chọn" - phân tích bằng tiếng Anh chứ không phải CLR và nó trở nên hợp lý và trong IMO mong muốn.
Adam Tolley

9
C # không, nhưng VB.NET thì có.
Jason

5
Điều này đã bị đánh đến chết, tuy nhiên, tôi không thể không đề cập đến sự hỗ trợ của tôi cho các đối số tùy chọn. Tôi đã trở nên khá quen thuộc với các đối số tùy chọn bằng cách tham chiếu thông qua việc đặt nullmặc định ( tôi đến từ PHP ) và thử nghiệm nullđể tiến hành điền vào đối số ( đối với những người quen thuộc, nghĩpreg_match() ) Dù sao, trong khi tôi hiểu từ điểm kỹ thuật hiện tại có thể là không thể, và PHP và C # là không thể so sánh được, nó vẫn sẽ là một công cụ " tốt đẹp " có sẵn.
Dan Lugg

Câu trả lời:


93

Như đã đề cập, điều này chỉ đơn giản là không được phép và tôi nghĩ nó có ý nghĩa rất tốt. Tuy nhiên, để thêm một số chi tiết, đây là một trích dẫn từ Đặc tả C # 4.0 , phần 21.1:

Các tham số chính thức của hàm tạo, phương thức, bộ chỉ mục và kiểu đại biểu có thể được khai báo tùy chọn:

fixed-tham số:
    thuộc tính opt tham số biến đổi tham số kiểu định danh opt
-default opt default-argument:
    = biểu thức

  • Một cố định tham số với một mặc định đối số là một tham số tùy chọn , trong khi một cố định tham số mà không có một mặc định đối số là một thông số cần thiết .
  • Một tham số bắt buộc không thể xuất hiện sau một tham số tùy chọn trong danh sách tham số chính thức .
  • Một refhoặc outtham số không thể có một đối số mặc định .

Ngoài ra, bạn có thể tạo quá tải với tham số ref / out. Vâng, bạn sẽ có hai định nghĩa hàm, nhưng nó sẽ hoàn thành những gì bạn đang theo đuổi
Chad

201

Không.

Một cách giải quyết là quá tải với một phương thức khác không có tham số out / ref và chỉ gọi phương thức hiện tại của bạn.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

Cập nhật: Nếu bạn có C # 7.0 , bạn có thể đơn giản hóa:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Cảm ơn @Oskar / @Reiner đã chỉ ra điều này.)


6
Bất kỳ ý tưởng cho giải pháp thanh lịch hơn so với tuyên bố temp / giả?
Louis Rhys

20
Điều gì không thanh lịch về điều đó? Có vẻ hoàn toàn tốt với tôi.
Neutrino

27
Có thể đàng hoàng, nhưng thanh lịch chắc chắn là trên một giải đấu khác. Thực tế không có giải pháp nào tốt hơn không có nghĩa đây là tình trạng hiện đại theo nghị định.
o0 '.

7
Giữ @ o0 '. Trong C # 7.0, bạn sẽ có thể làm điều này : return SomeMethod(out string temp). Xem thêm tại đây: blog.msdn.microsoft.com/dotnet/2016/08/24/ trên
Oskar

7
Trong C # 7.0, bạn có thể sử dụng biến chỉ ghi tạm thời như:return SomeMethod(out _);
Reiner

64

Không, nhưng một phương án tuyệt vời khác là phương thức sử dụng lớp mẫu chung cho các tham số tùy chọn như sau:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Sau đó, bạn có thể sử dụng nó như sau:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}

19
Đó là một giải pháp rất hợp lý, nhưng một điều cần lưu ý là trình biên dịch sẽ không thực thi yêu cầu rằng tham số out được gán trước khi thoát khỏi phương thức.
Ken Smith

2
Tôi thích nó, nhưng nếu bạn không muốn tạo một lớp mới, bạn có thể mô phỏng nó bằng cách chuyển vào một mảng phần tử duy nhất.
bảo vệ zumalififard 6/215

30

Thực sự có một cách để làm điều này được C # cho phép. Điều này trở lại với C ++ và vi phạm cấu trúc hướng đối tượng đẹp của C #.

SỬ DỤNG PHƯƠNG PHÁP NÀY VỚI CẨN THẬN!

Đây là cách bạn khai báo và viết hàm của bạn với một tham số tùy chọn:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Sau đó gọi hàm như thế này:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

Để biên dịch nó, bạn sẽ cần bật mã không an toàn trong các tùy chọn dự án. Đây là một giải pháp thực sự hóc búa thường không nên được sử dụng, nhưng nếu bạn cho một quyết định kỳ lạ, bí ẩn, lấy cảm hứng từ quản lý, THỰC SỰ cần một tham số tùy chọn trong C #, thì điều này sẽ cho phép bạn làm điều đó.


6

ICYMI: Được bao gồm trong các tính năng mới cho C # 7.0 được liệt kê ở đây , "loại bỏ" hiện được phép dưới dạng tham số dưới dạng _, để cho phép bạn bỏ qua các tham số bạn không quan tâm:

p.GetCoordinates(out var x, out _); // I only care about x

PS nếu bạn cũng nhầm lẫn với phần "out var x", hãy đọc tính năng mới về "Biến ngoài" trên liên kết.


là nó _ hay * "p.GetCoordins (out int x, out *); // Tôi chỉ quan tâm đến x"
Onur Topal

Có vẻ như tôi đang tìm một phiên bản cũ hơn của tài liệu.
Onur Topal

2

Không, nhưng bạn có thể sử dụng một đại biểu (ví dụ Action) thay thế.

Lấy cảm hứng một phần bởi câu trả lời của Robin R khi đối mặt với một tình huống mà tôi nghĩ rằng tôi muốn có một tham số ngoài tùy chọn, thay vào đó tôi đã sử dụng một Actionđại biểu. Tôi đã mượn mã ví dụ của anh ấy để sửa đổi để sử dụng Action<int>để hiển thị sự khác biệt và tương đồng:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

Điều này có lợi thế là biến tùy chọn xuất hiện trong nguồn dưới dạng int bình thường (trình biên dịch bao bọc nó trong một lớp đóng, thay vì chúng ta gói nó một cách rõ ràng trong một lớp do người dùng định nghĩa).

Biến cần khởi tạo rõ ràng vì trình biên dịch không thể giả sử rằng Action sẽ được gọi trước khi thoát lệnh gọi hàm.

Nó không phù hợp với tất cả các trường hợp sử dụng, nhưng hoạt động tốt cho trường hợp sử dụng thực sự của tôi (một chức năng cung cấp dữ liệu cho thử nghiệm đơn vị và khi thử nghiệm đơn vị mới cần truy cập vào một số trạng thái bên trong không có trong giá trị trả về).


0

Sử dụng phương thức quá tải mà không có tham số out để gọi phương thức có tham số out cho C # 6.0 trở xuống. Tôi không chắc tại sao C # 7.0 cho .NET Core thậm chí là câu trả lời chính xác cho chủ đề này khi được hỏi cụ thể liệu C # 4.0 có thể có tham số ngoài tùy chọn không. Câu trả lời là không!


-2

Thế còn như thế này?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

Bạn vẫn phải truyền một giá trị cho tham số từ C # nhưng đó là một tham số ref tùy chọn.


8
Bạn vẫn phải truyền một giá trị cho tham số từ C # ... Điều này làm cho nó không phải là tùy chọn.
Arturo Torres Sánchez

C # bỏ qua [Optional]chú thích. Điều này không giúp được gì.
ToolmakerSteve

-4
void foo(ref int? n)
{
    return null;
}

1
Vui lòng thêm một số giải thích về mã của bạn để làm cho mọi người hiểu khái niệm này một cách dễ dàng
techspider

1
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do tại sao và / hoặc cách nó trả lời câu hỏi sẽ cải thiện đáng kể giá trị lâu dài của nó. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số lời giải thích.
Toby Speight

2
Điều này sẽ dẫn đến một lỗi cú pháp vì phương thức có kiểu trả về void. Ngoài ra, không trả lời câu hỏi.
Nathan Montez
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.