Argumen opsional keluar / ref C # 4.0

212

Apakah C # 4.0 mengizinkan opsi outatau refargumen?

Joe Daley
sumber
2
Weell, C ++ secara efektif memilikinya untuk parameter "out" - Anda dapat memiliki argumen alamat yang diinisialisasi menjadi nol dan cukup umum untuk menulis kode pustaka yang hanya akan mengisi struktur pengembalian seperti itu jika penunjuknya bukan nol. Itu idiom kembali menggunakan null untuk "argumen opsional" di C API.
Andy Dent
53
@ Ed dan semuanya: mengapa ini tidak masuk akal? Jika fungsi "mengembalikan" nilai melalui "keluar", saya tidak ingin dipaksa untuk menerimanya. Sekarang saya tahu bahwa karena alasan teknis kompiler masih harus memasukkan sesuatu, tetapi tidak ada alasan mengapa itu tidak bisa hanya membuat lokal boneka untuk saya di belakang saya.
Roman Starkov
5
Mungkin tidak masuk akal dari sudut pandang bagaimana hal-hal diimplementasikan atau apa parameter opsional sebenarnya. Tapi seperti kata romkyn, akan sangat menyenangkan untuk memiliki "opsional argumen keluar" - menguraikan bahwa dalam bahasa Inggris daripada CLR dan menjadi masuk akal dan dalam IMO diinginkan.
Adam Tolley
9
C # tidak, tetapi VB.NET melakukannya.
Jason
5
Ini telah dipukuli sampai mati, bagaimanapun, saya tidak bisa tidak menyebutkan dukungan saya untuk argumen keluar opsional. Saya sudah cukup terbiasa dengan argumen opsional dengan referensi melalui pengaturan nulldefault ( saya berasal dari PHP ) dan pengujian untuk nullmelanjutkan dengan mengisi argumen ( bagi mereka yang akrab, pikirkanpreg_match() ) Bagaimanapun, sementara saya mengerti dari sudut pandang teknis, saat ini mungkin tidak mungkin, dan bahwa PHP dan C # agak tak tertandingi, itu masih akan menjadi alat " bagus " untuk tersedia.
Dan Lugg

Jawaban:

93

Seperti yang telah disebutkan, ini sama sekali tidak diperbolehkan dan saya pikir itu masuk akal. Namun, untuk menambahkan lebih banyak detail, berikut adalah kutipan dari Spesifikasi C # 4.0 , bagian 21.1:

Parameter formal konstruktor, metode, pengindeks dan tipe delegasi dapat dinyatakan opsional:

fixed-parameter:
    atribut opt parameter-modifier opt tipe identifier default-argumen opt
default-argument:
    = ekspresi

  • Sebuah fixed-parameter dengan default-argumen adalah parameter opsional , sedangkan fixed-parameter tanpa default-argumen adalah parameter yang diperlukan .
  • Parameter yang diperlukan tidak dapat muncul setelah parameter opsional dalam daftar parameter-formal .
  • A refatau outparameter tidak dapat memiliki argumen default .
Tomas Petricek
sumber
Atau, Anda dapat membuat kelebihan dengan parameter ref / out. Ya, Anda akan memiliki dua definisi fungsi, tetapi itu akan mencapai apa yang Anda cari
Chad
201

Tidak.

Solusinya adalah kelebihan dengan metode lain yang tidak memiliki parameter out / ref, dan yang hanya memanggil metode Anda saat ini.

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

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

Pembaruan: Jika Anda memiliki C # 7.0 , Anda dapat menyederhanakan:

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

(Terima kasih @Oskar / @Reiner karena menunjukkan ini.)

Dunc
sumber
6
ada ide untuk solusi yang lebih elegan daripada mendeklarasikan temp / dummy?
Louis Rhys
20
Apa yang tidak elegan tentang itu? Terlihat sangat baik bagi saya.
Neutrino
27
Mungkin layak, tetapi elegan jelas di liga lain. Fakta bahwa tidak ada solusi yang lebih baik tidak berarti ini adalah yang terbaik dengan keputusan.
o0 '.
7
Tunggu @ o0 '. Dalam C # 7.0 Anda akan dapat melakukan hal ini: return SomeMethod(out string temp). Lihat lebih lanjut di sini: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar
7
Dalam C # 7.0 Anda dapat menggunakan variabel hanya-tulis sementara seperti:return SomeMethod(out _);
Reiner
64

Tidak, tetapi alternatif hebat lainnya adalah memiliki metode yang menggunakan kelas templat generik untuk parameter opsional sebagai berikut:

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

Maka Anda dapat menggunakannya sebagai berikut:

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);
}
Robin R
sumber
19
Ini adalah solusi yang sangat masuk akal, tetapi satu hal yang perlu diperhatikan adalah bahwa kompiler tidak akan menegakkan persyaratan bahwa parameter keluar ditugaskan sebelum keluar dari metode.
Ken Smith
2
Saya menyukainya, tetapi jika Anda tidak ingin membuat kelas baru, Anda dapat mensimulasikannya dengan mengirimkan satu elemen array.
zumalifeguard
30

Sebenarnya ada cara untuk melakukan ini yang diizinkan oleh C #. Ini akan kembali ke C ++, dan agak melanggar struktur Berorientasi Objek C # yang bagus.

GUNAKAN METODE INI DENGAN AWAS!

Inilah cara Anda mendeklarasikan dan menulis fungsi Anda dengan parameter opsional:

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.
    }
}

Kemudian panggil fungsi seperti ini:

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

Untuk mengkompilasi ini, Anda harus mengaktifkan kode yang tidak aman dalam opsi proyek. Ini adalah solusi yang sangat aneh yang biasanya tidak boleh digunakan, tetapi jika Anda untuk beberapa keputusan aneh, misterius, misterius, diilhami manajemen, BENAR-BENAR membutuhkan parameter keluar opsional di C #, maka ini akan memungkinkan Anda untuk melakukan hal itu.

wizard07KSU
sumber
6

ICYMI: Termasuk pada fitur baru untuk C # 7.0 yang disebutkan di sini , "discards" sekarang diizinkan sebagai parameter dalam bentuk _, untuk membiarkan Anda mengabaikan parameter yang tidak Anda pedulikan:

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

NB jika Anda juga bingung dengan bagian "out var x", baca fitur baru tentang "Variabel Keluar" pada tautan juga.

jbdeguzman
sumber
apakah itu _ atau * "p.GetCoordinate (out int x, out *); // Saya hanya peduli tentang x"
Onur Topal
Sepertinya saya sedang mencari versi dokumen yang lebih lama.
Onur Topal
2

Tidak, tetapi Anda dapat menggunakan delegasi (mis. Action) Sebagai alternatif.

Terinspirasi sebagian oleh jawaban Robin R ketika menghadapi situasi di mana saya pikir saya ingin parameter opsional keluar, saya malah menggunakan Actiondelegasi. Saya telah meminjam kode contohnya untuk dimodifikasi agar dapat digunakan Action<int>untuk menunjukkan perbedaan dan persamaan:

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);
}

Ini memiliki keuntungan bahwa variabel opsional muncul di sumber sebagai int normal (kompiler membungkusnya dalam kelas penutupan, daripada kita membungkusnya secara eksplisit dalam kelas yang ditentukan pengguna).

Variabel perlu inisialisasi eksplisit karena kompiler tidak dapat berasumsi bahwa Actionakan dipanggil sebelum panggilan fungsi keluar.

Ini tidak cocok untuk semua kasus penggunaan, tetapi bekerja dengan baik untuk kasus penggunaan nyata saya (fungsi yang menyediakan data untuk tes unit, dan di mana tes unit baru membutuhkan akses ke beberapa keadaan internal yang tidak ada dalam nilai balik).

Steve
sumber
0

Gunakan metode kelebihan beban tanpa parameter keluar untuk memanggil yang dengan parameter keluar untuk C # 6.0 dan lebih rendah. Saya tidak yakin mengapa C # 7.0 untuk .NET Core bahkan merupakan jawaban yang tepat untuk utas ini ketika ditanya apakah C # 4.0 dapat memiliki parameter keluar opsional. Jawabannya adalah tidak!

Mr_CSharp
sumber
-2

Bagaimana dengan ini?

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

Anda masih harus meneruskan nilai ke parameter dari C # tetapi ini merupakan parameter referensi opsional.

Aaron L.
sumber
8
"Anda masih harus meneruskan nilai ke parameter dari C #" ... Ini membuatnya tidak opsional.
Arturo Torres Sánchez
C # mengabaikan [Optional]anotasi. Ini tidak membantu.
ToolmakerSteve
-4
void foo(ref int? n)
{
    return null;
}
pengguna6469927
sumber
1
Silakan tambahkan beberapa penjelasan pada kode Anda untuk membuat semua orang memahami konsep dengan mudah
techspider
1
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana ia menjawab pertanyaan akan secara signifikan meningkatkan nilai jangka panjangnya. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan.
Toby Speight
2
Ini akan menghasilkan kesalahan sintaksis karena metode memiliki tipe pengembalian batal. Juga, tidak menjawab pertanyaan.
Nathan Montez