C # 4.0 อาร์กิวเมนต์ตัวเลือกไม่ระบุ / อ้างอิง


212

C # 4.0 อนุญาตoutหรือไม่ก็ได้หรือrefขัดแย้งกัน?


2
Weell, C ++ มีประสิทธิภาพสำหรับพารามิเตอร์ "out" - คุณสามารถมีอาร์กิวเมนต์ที่กำหนดค่าเริ่มต้นให้เป็นโมฆะและเป็นเรื่องธรรมดามากที่จะเขียนโค้ดไลบรารีที่จะเติมโครงสร้างการส่งคืนเท่านั้นหากตัวชี้ไม่ใช่โมฆะ นั่นเป็นสำนวนกลับไปใช้ null สำหรับ "อาร์กิวเมนต์ตัวเลือก" ใน C APIs
Andy Dent

53
@Ed และทุกคน: ทำไมเรื่องนี้ถึงไม่สมเหตุสมผล หากฟังก์ชั่น "คืน" ค่าผ่าน "ออก" ฉันไม่ต้องการถูกบังคับให้ยอมรับมัน ตอนนี้ฉันรู้แล้วว่าสำหรับเหตุผลทางเทคนิคคอมไพเลอร์ยังคงต้องผ่านบางสิ่งบางอย่างใน แต่ไม่มีเหตุผลว่าทำไมมันไม่เพียงสร้างหุ่นท้องถิ่นสำหรับฉันหลังหลังของฉัน
Roman Starkov

5
บางทีมันอาจไม่มีเหตุผลจากมุมมองว่ามีการนำสิ่งต่าง ๆ ไปใช้อย่างไรหรือพารามิเตอร์ทางเลือกคืออะไร แต่เช่น romkyns กล่าวว่ามันจะดีจริงๆที่จะมี "ข้อโต้แย้งออกไม่จำเป็น" - แยกว่าในภาษาอังกฤษมากกว่า CLR และมันจะกลายเป็นเหตุผลและเป็นที่ต้องการ IMO
Adam Tolley

9
C # ไม่ได้ แต่ VB.NET ทำ
Jason

5
เรื่องนี้พ่ายแพ้ต่อความตาย แต่ฉันอดไม่ได้ที่จะพูดถึงการสนับสนุนของฉันสำหรับข้อโต้แย้งที่ไม่จำเป็น ฉันคุ้นเคยกับอาร์กิวเมนต์ที่เป็นทางเลือกโดยการอ้างอิงผ่านการตั้งค่าnullเริ่มต้น ( ฉันมาจาก PHP ) และการทดสอบnullเพื่อดำเนินการกับการสร้างอาร์กิวเมนต์ ( สำหรับผู้ที่คุ้นเคยคิดว่าpreg_match() ) อย่างไรก็ตามในขณะที่ฉันเข้าใจจากประเด็นทางเทคนิค เป็นไปไม่ได้และ PHP และ C # นั้นค่อนข้างหาที่เปรียบมิได้มันจะเป็นเครื่องมือ "ที่ดี " ที่มีอยู่
Dan Lugg

คำตอบ:


93

ดังที่ได้กล่าวไปแล้วสิ่งนี้ไม่ได้รับอนุญาตและฉันคิดว่ามันสมเหตุสมผลดี อย่างไรก็ตามหากต้องการเพิ่มรายละเอียดเพิ่มเติมนี่คือคำพูดจากข้อกำหนดC # 4.0ส่วนที่ 21.1:

พารามิเตอร์ทางการของผู้สร้างวิธีการดัชนีและประเภทตัวแทนสามารถประกาศตัวเลือก:

fixed-parameter:
    attributes opt opt -modifier opt type identifier default-argument opt
default-argument:
    = expression

  • คงพารามิเตอร์ที่มีค่าเริ่มต้นอาร์กิวเมนต์เป็นพารามิเตอร์ตัวเลือกในขณะที่คงที่พารามิเตอร์โดยไม่ต้องเริ่มต้นอาร์กิวเมนต์เป็นพารามิเตอร์ที่จำเป็น
  • พารามิเตอร์ที่จำเป็นไม่สามารถปรากฏหลังจากพารามิเตอร์ในทางการพารามิเตอร์รายการ
  • refหรือoutพารามิเตอร์ไม่สามารถมีค่าเริ่มต้นอาร์กิวเมนต์

หรือคุณสามารถสร้างโอเวอร์โหลดด้วยพารามิเตอร์ ref / out ใช่คุณจะมีคำจำกัดความของฟังก์ชั่นสองอย่าง แต่มันจะสำเร็จสิ่งที่คุณตามมา
Chad

201

เลขที่

วิธีแก้ปัญหาคือการโอเวอร์โหลดด้วยวิธีอื่นที่ไม่มีพารามิเตอร์ out / ref และเพียงเรียกวิธีปัจจุบันของคุณ

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

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

อัปเดต: หากคุณมีC # 7.0คุณสามารถทำให้:

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

(ขอบคุณ @Oskar / @Reiner สำหรับการชี้เรื่องนี้)


6
ความคิดสำหรับวิธีการแก้ปัญหาที่สง่างามกว่าการประกาศ temp / dummy?
Louis Rhys

20
มีอะไรไม่สง่าเกี่ยวกับที่? ดูดีอย่างสมบูรณ์แบบสำหรับฉัน
Neutrino

27
อาจจะดี แต่ความสง่างามนั้นแน่นอนในลีกอื่น ความจริงที่ไม่มีวิธีแก้ปัญหาที่ดีกว่าไม่ได้หมายความว่านี่เป็นคำสั่งที่ล้ำสมัย
o0 '

7
กดค้างที่ @ o0 ' ใน C # 7.0 return SomeMethod(out string temp)คุณจะสามารถที่จะทำเช่นนี้: ดูเพิ่มเติมได้ที่นี่: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar

7
ใน C # 7.0 คุณสามารถใช้ตัวแปรแบบเขียนอย่างเดียวชั่วคราวเช่น:return SomeMethod(out _);
Reiner

64

ไม่ แต่ทางเลือกที่ดีอีกวิธีหนึ่งคือการใช้วิธีนี้ใช้คลาสเทมเพลตทั่วไปสำหรับพารามิเตอร์ทางเลือกดังต่อไปนี้:

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

จากนั้นคุณสามารถใช้มันได้ดังต่อไปนี้:

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
มันเป็นคำตอบที่สมเหตุสมผล แต่สิ่งหนึ่งที่ต้องระวังคือคอมไพเลอร์จะไม่บังคับใช้ข้อกำหนดที่พารามิเตอร์ out ได้รับมอบหมายก่อนที่จะออกจากเมธอด
Ken Smith

2
ฉันชอบมัน แต่ถ้าคุณไม่ต้องการสร้างคลาสใหม่คุณสามารถจำลองมันได้โดยการส่งผ่านในอาร์เรย์องค์ประกอบเดียว
zumalifeguard

30

จริงๆแล้วมีวิธีการทำเช่นนี้ที่ C # อนุญาต สิ่งนี้จะได้รับกลับไปที่ C ++ และเป็นการละเมิดโครงสร้างเชิงวัตถุที่ดีของ C #

ใช้วิธีนี้ด้วยความระมัดระวัง!

นี่คือวิธีที่คุณประกาศและเขียนฟังก์ชันของคุณด้วยพารามิเตอร์ทางเลือก:

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

จากนั้นเรียกใช้ฟังก์ชันดังนี้:

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

ในการรวบรวมสิ่งนี้คุณจะต้องเปิดใช้งานรหัสที่ไม่ปลอดภัยในตัวเลือกโครงการ นี่เป็นวิธีแฮ็คจริงๆที่ไม่ควรใช้ แต่ถ้าคุณมีการตัดสินใจที่แปลกประหลาดลึกลับลึกลับและได้แรงบันดาลใจจากการจัดการจริงๆแล้วต้องมีพารามิเตอร์เสริมใน C # ดังนั้นสิ่งนี้จะช่วยให้คุณทำเช่นนั้นได้


6

ICYMI: รวมอยู่ในคุณสมบัติใหม่สำหรับ C # 7.0 ที่ระบุที่นี่ "ทิ้ง" ตอนนี้ได้รับอนุญาตเป็นพารามิเตอร์ออกในรูปแบบของ _ เพื่อให้คุณไม่สนใจพารามิเตอร์ที่คุณไม่สนใจ:

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

ป.ล. ถ้าคุณสับสนกับส่วน "ออก var x" อ่านคุณสมบัติใหม่เกี่ยวกับ "ตัวแปรออก" ที่ลิงค์เช่นกัน


มันคือ _ หรือ * "p.GetCoordinates (ออก int x, out *); // ฉันสนใจแค่ x"
Onur Topal

ดูเหมือนว่าฉันกำลังดูเอกสารเวอร์ชันเก่ากว่าอยู่
Onur Topal

2

ไม่ แต่คุณสามารถใช้ผู้รับมอบสิทธิ์ (เช่นAction) เป็นทางเลือก

ได้แรงบันดาลใจส่วนหนึ่งจากคำตอบของ Robin R เมื่อต้องเผชิญกับสถานการณ์ที่ฉันคิดว่าฉันต้องการพารามิเตอร์ตัวเลือกเพิ่มเติมฉันจึงใช้Actionตัวแทน ฉันยืมรหัสตัวอย่างของเขาเพื่อแก้ไขเพื่อใช้Action<int>ในการแสดงความแตกต่างและความคล้ายคลึงกัน:

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

สิ่งนี้มีความได้เปรียบที่ตัวแปรทางเลือกจะปรากฏในแหล่งที่มาเป็น int ปกติ (คอมไพเลอร์ล้อมรอบในคลาสที่ปิดมากกว่าที่เราจะห่อมันอย่างชัดเจนในคลาสที่ผู้ใช้กำหนดเอง)

ตัวแปรต้องการการกำหนดค่าเริ่มต้นอย่างชัดเจนเนื่องจากคอมไพเลอร์ไม่สามารถสันนิษฐานได้ว่าActionจะมีการเรียกก่อนที่การเรียกใช้ฟังก์ชันจะออก

มันไม่เหมาะสำหรับทุกกรณีการใช้งาน แต่ทำงานได้ดีสำหรับกรณีการใช้งานจริงของฉัน (ฟังก์ชั่นที่ให้ข้อมูลสำหรับการทดสอบหน่วยและที่การทดสอบหน่วยใหม่จำเป็นต้องเข้าถึงสถานะภายในบางอย่างที่ไม่อยู่ในค่าส่งคืน)


0

ใช้วิธีโอเวอร์โหลดโดยไม่มีพารามิเตอร์ out เพื่อเรียกใช้วิธีที่มีพารามิเตอร์ out สำหรับ C # 6.0 และต่ำกว่า ฉันไม่แน่ใจว่าทำไม C # 7.0 สำหรับ. NET Core จึงเป็นคำตอบที่ถูกต้องสำหรับเธรดนี้เมื่อมีการถามเป็นการเฉพาะว่า C # 4.0 สามารถมีพารามิเตอร์เผื่อเลือกหรือไม่ คำตอบคือไม่!


-2

แล้วอันนี้ล่ะ

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

คุณยังต้องส่งค่าไปยังพารามิเตอร์จาก C # แต่เป็นพารามิเตอร์อ้างอิงที่เป็นตัวเลือก


8
“ คุณยังคงต้องส่งค่าไปยังพารามิเตอร์จาก C #” ... นี่ทำให้ไม่จำเป็น
Arturo Torres Sánchez

C # ละเว้น[Optional]คำอธิบายประกอบ สิ่งนี้ไม่ได้ช่วยอะไร
ToolmakerSteve

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

1
โปรดเพิ่มคำอธิบายลงในโค้ดของคุณเพื่อให้ทุกคนเข้าใจแนวคิดได้ง่ายขึ้น
พัฒนาเทคโนโลยี

1
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีการตอบคำถามนั้นจะช่วยปรับปรุงมูลค่าระยะยาวได้อย่างมีนัยสำคัญ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบาย
Toby Speight

2
สิ่งนี้จะส่งผลให้เกิดข้อผิดพลาดทางไวยากรณ์เนื่องจากเมธอดมีชนิดส่งคืนเป็นโมฆะ ยังไม่ตอบคำถาม
Nathan Montez
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.