อันไหนดีกว่าคืนค่าหรือพารามิเตอร์ออก


147

หากเราต้องการรับค่าจากวิธีใดเราสามารถใช้ค่าส่งคืนเช่นนี้

public int GetValue(); 

หรือ:

public void GetValue(out int x);

ฉันไม่เข้าใจความแตกต่างระหว่างพวกเขาดังนั้นไม่รู้ว่าอะไรดีกว่า คุณช่วยอธิบายสิ่งนี้ให้ฉันได้ไหม

ขอบคุณ.


3
ฉันหวังว่า C # จะมีค่าตอบแทนหลายอย่างเช่น Python
กับดัก

12
@Trap คุณสามารถส่งคืน a Tupleถ้าคุณต้องการ แต่ฉันทามติทั่วไปคือถ้าคุณต้องการส่งคืนมากกว่าหนึ่งสิ่งสิ่งที่มักจะเกี่ยวข้องอย่างใดและความสัมพันธ์นั้นมักจะแสดงที่ดีที่สุดในชั้นเรียน
Pharap

2
@Phap Tuples ใน C # ในรูปแบบปัจจุบันมันน่าเกลียด แต่นั่นเป็นเพียงความคิดเห็นของฉัน ในทางกลับกัน "มติทั่วไป" ไม่ได้หมายถึงอะไรจากมุมมองการใช้งานและผลผลิต คุณจะไม่สร้างคลาสเพื่อคืนค่าสองสามค่าด้วยเหตุผลเดียวกับที่คุณจะไม่สร้างคลาสเพื่อคืนค่าสองสามค่าเป็นพารามิเตอร์ ref / out
กับดัก

@Trap มันreturn Tuple.Create(x, y, z);ไม่น่าเกลียดหรอก นอกจากนี้ในช่วงสายของวันที่พวกเขาได้แนะนำระดับภาษา เหตุผลที่ฉันจะไม่สร้างคลาสเพื่อส่งคืนค่าจากพารามิเตอร์ ref / out เป็นเพราะ par / ref / out ทำให้เข้าใจได้ง่ายสำหรับ structable ที่ไม่แน่นอนขนาดใหญ่ (เช่นเมทริกซ์) หรือค่าทางเลือกและอันหลังเป็นที่ถกเถียงกัน
Pharap

@Pharap C # ทีมกำลังมองหาที่จะแนะนำสิ่งอันดับในระดับภาษา ในขณะที่มีการต้อนรับตัวเลือกมากมายทั้งใน. NET ที่ท่วมท้นในขณะนี้ - ประเภทที่ไม่ระบุชื่อ,. NET Tuple<>และ C # tuples! ฉันแค่อยากให้ C # อนุญาตให้ส่งคืนชนิดไม่ระบุชื่อจากวิธีที่มีประเภทอนุมานคอมไพเลอร์ (เช่นautoใน Dlang)
nawfal

คำตอบ:


153

ค่าส่งคืนเป็นตัวเลือกที่ถูกต้องเกือบทุกครั้งเมื่อวิธีไม่มีสิ่งอื่นใดที่จะส่งคืน (ในความเป็นจริงฉันไม่สามารถคิดว่ากรณีใด ๆ ที่ฉันเคยต้องการวิธีการที่เป็นโมฆะกับoutพารามิเตอร์ถ้าผมมีทางเลือก. C # 7 Deconstructวิธีการสำหรับภาษาที่สนับสนุนโครงสร้างทำหน้าที่เป็นข้อยกเว้นมากหายากมากกฎนี้ .)

นอกเหนือจากสิ่งอื่นมันจะหยุดผู้เรียกไม่ให้ประกาศตัวแปรแยกต่างหาก:

int foo;
GetValue(out foo);

VS

int foo = GetValue();

ค่า Out ยังป้องกันการโยงวิธีเช่นนี้:

Console.WriteLine(GetValue().ToString("g"));

(อันที่จริงนั่นเป็นปัญหาอย่างหนึ่งของ setters เช่นกันและนั่นเป็นสาเหตุที่รูปแบบตัวสร้างใช้วิธีการที่ส่งคืนตัวสร้างเช่นmyStringBuilder.Append(xxx).Append(yyy))

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

ส่งคืนค่า FTW

แก้ไข: ในแง่ของสิ่งที่เกิดขึ้น ...

โดยทั่วไปเมื่อคุณผ่านการโต้แย้งสำหรับพารามิเตอร์ "ออก" คุณจะต้องผ่านตัวแปร (องค์ประกอบอาเรย์ถูกจัดประเภทเป็นตัวแปรด้วย) วิธีการที่คุณเรียกไม่มีตัวแปร "ใหม่" ในสแต็กสำหรับพารามิเตอร์ - มันใช้ตัวแปรของคุณสำหรับการจัดเก็บ การเปลี่ยนแปลงใด ๆ ในตัวแปรจะมองเห็นได้ทันที นี่คือตัวอย่างที่แสดงความแตกต่าง:

using System;

class Test
{
    static int value;

    static void ShowValue(string description)
    {
        Console.WriteLine(description + value);
    }

    static void Main()
    {
        Console.WriteLine("Return value test...");
        value = 5;
        value = ReturnValue();
        ShowValue("Value after ReturnValue(): ");

        value = 5;
        Console.WriteLine("Out parameter test...");
        OutParameter(out value);
        ShowValue("Value after OutParameter(): ");
    }

    static int ReturnValue()
    {
        ShowValue("ReturnValue (pre): ");
        int tmp = 10;
        ShowValue("ReturnValue (post): ");
        return tmp;
    }

    static void OutParameter(out int tmp)
    {
        ShowValue("OutParameter (pre): ");
        tmp = 10;
        ShowValue("OutParameter (post): ");
    }
}

ผล:

Return value test...
ReturnValue (pre): 5
ReturnValue (post): 5
Value after ReturnValue(): 10
Out parameter test...
OutParameter (pre): 5
OutParameter (post): 10
Value after OutParameter(): 10

ความแตกต่างอยู่ที่ขั้นตอน "โพสต์" - เช่นหลังจากตัวแปรท้องถิ่นหรือพารามิเตอร์มีการเปลี่ยนแปลง ในการทดสอบ ReturnValue สิ่งนี้ไม่สร้างความแตกต่างให้กับvalueตัวแปรแบบคงที่ ในการทดสอบ OutParameter valueตัวแปรจะถูกเปลี่ยนตามบรรทัดtmp = 10;


2
คุณทำให้ฉันเชื่อว่าค่าตอบแทนคืนนั้นดีกว่ามาก :) แต่ฉันก็ยังสงสัยว่าเกิดอะไรขึ้น "ในเชิงลึก" ฉันหมายถึงค่าส่งคืนและพารามิเตอร์ out แตกต่างกันในวิธีการสร้างกำหนดและส่งคืนหรือไม่
Quan เชียงใหม่

1
TryParse เป็นตัวอย่างที่ดีที่สุดเมื่อใช้งานพารามิเตอร์ที่เหมาะสม & สะอาด ถึงแม้ว่าผมจะได้ใช้มันในกรณีพิเศษเช่นถ้า (WorkSucceeded (จากรายการ <สตริง> ข้อผิดพลาด) ซึ่งเป็นพื้นรูปแบบเดียวกับ TryParse
ชาดแกรนท์

2
คำตอบที่ไม่ช่วยเหลือดี ตามที่อธิบายไว้ในความคิดเห็นของคำถามนี้พารามิเตอร์ out มีข้อดีที่มีประโยชน์หลายประการ การตั้งค่าระหว่างออกกับผลตอบแทนควรขึ้นอยู่กับสถานการณ์
aaronsnoswell

2
@aaronsnoswell: ความเห็นไหนที่แม่นยำ? จำไว้ว่าตัวอย่างของการDictionary.TryGetValueเป็นไม่ได้บังคับนี่เป็นนั่นไม่ใช่วิธีการที่เป็นโมฆะ คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงต้องการoutพารามิเตอร์แทนค่าส่งคืน (ถึงแม้TryGetValueฉันจะชอบค่าที่ส่งคืนซึ่งมีข้อมูลเอาท์พุททั้งหมดเป็นการส่วนตัวดู NodaTime ParseResult<T>เพื่อเป็นตัวอย่างของวิธีที่ฉันได้ออกแบบไว้)
Jon Skeet

2
@ จิม: ฉันเดาว่าเราจะต้องเห็นด้วยที่จะไม่เห็นด้วย ในขณะที่ฉันเห็นจุดของคุณเกี่ยวกับการตอกคนเหนือหัวกับเรื่องนี้ก็ยังทำให้มันเป็นมิตรน้อยสำหรับผู้ที่ไม่ทราบว่าสิ่งที่พวกเขากำลังทำ สิ่งที่ดีเกี่ยวกับข้อยกเว้นแทนที่จะส่งคืนค่าคือคุณไม่สามารถเพิกเฉยได้ง่ายและดำเนินการต่อราวกับไม่มีอะไรเกิดขึ้น ... ในขณะที่ทั้งค่าส่งคืนและoutพารามิเตอร์คุณไม่สามารถทำอะไรกับค่าได้
Jon Skeet

26

มีอะไรที่ดีกว่านี้ขึ้นอยู่กับสถานการณ์เฉพาะของคุณ หนึ่งในเหตุผลที่outมีอยู่คือการอำนวยความสะดวกในการคืนค่าหลายค่าจากการเรียกใช้วิธีการเดียว:

public int ReturnMultiple(int input, out int output1, out int output2)
{
    output1 = input + 1;
    output2 = input + 2;

    return input;
}

ดังนั้นหนึ่งไม่ได้นิยามดีกว่าอีก แต่โดยปกติคุณจะต้องการใช้การส่งคืนแบบง่ายเว้นแต่คุณจะมีสถานการณ์ข้างต้นเป็นตัวอย่าง

แก้ไข: นี่คือตัวอย่างที่แสดงหนึ่งในเหตุผลที่คำหลักนั้นมีอยู่ สิ่งที่กล่าวมาข้างต้นนั้นไม่ถือว่าเป็นการปฏิบัติที่ดีที่สุด


2
ฉันไม่เห็นด้วยกับสิ่งนี้ทำไมคุณไม่กลับโครงสร้างข้อมูลด้วย 4 ints? นี่มันสับสนจริงๆ
Chad Grant

1
เห็นได้ชัดว่ามีหลายวิธี (และดีกว่า) ในการคืนค่าหลายค่าฉันแค่ให้เหตุผลว่าทำไม OP จึงมีอยู่ในตอนแรก
pyrocumulus

2
ฉันเห็นด้วยกับ @Cloud เพียงเพราะมันไม่ใช่วิธีที่ดีที่สุดไม่ได้หมายความว่าไม่ควรมีอยู่
Cerebrus

รหัสจะไม่ได้รวบรวมสำหรับหนึ่ง ... และอย่างน้อยก็ควรพูดถึงว่ามันถือว่าการปฏิบัติที่ไม่ดี / ไม่ต้องการ
Chad Grant

1
มันจะไม่รวบรวม? และทำไมเป็นเช่นนั้น? ตัวอย่างนี้สามารถรวบรวมได้อย่างสมบูรณ์แบบ และได้โปรดฉันกำลังให้ตัวอย่างว่าทำไมไวยากรณ์ / คำหลักที่เฉพาะเจาะจงมีอยู่ไม่ใช่บทเรียนในการปฏิบัติที่ดีที่สุด
pyrocumulus

23

โดยทั่วไปคุณควรเลือกที่จะส่งคืนค่ามากกว่าพารามิเตอร์ออก Params เป็นสิ่งที่จำเป็นหากคุณพบว่าคุณกำลังเขียนโค้ดที่ต้องทำ 2 สิ่ง ตัวอย่างที่ดีของสิ่งนี้คือรูปแบบลอง (เช่น Int32.TryParse)

ลองพิจารณาสิ่งที่ผู้โทรในสองวิธีของคุณจะต้องทำ สำหรับตัวอย่างแรกฉันสามารถเขียนสิ่งนี้ ...

int foo = GetValue();

สังเกตว่าฉันสามารถประกาศตัวแปรและกำหนดมันผ่านวิธีการของคุณในหนึ่งบรรทัด สำหรับตัวอย่างที่ 2 ดูเหมือนว่านี้ ...

int foo;
GetValue(out foo);

ตอนนี้ฉันถูกบังคับให้ประกาศตัวแปรของฉันล่วงหน้าและเขียนโค้ดของฉันมากกว่าสองบรรทัด

ปรับปรุง

ที่ที่ควรมองเมื่อถามคำถามประเภทนี้คือแนวทางการออกแบบ. NET Framework หากคุณมีเวอร์ชันหนังสือคุณสามารถดูคำอธิบายประกอบโดย Anders Hejlsberg และคนอื่น ๆ ในหัวข้อนี้ (หน้า 184-185) แต่เวอร์ชันออนไลน์อยู่ที่นี่ ...

http://msdn.microsoft.com/en-us/library/ms182131(VS.80).aspx

หากคุณพบว่าตัวเองต้องการคืนสองสิ่งจาก API ดังนั้นการรวมมันไว้ใน struct / class จะดีกว่า param out


คำตอบที่ยอดเยี่ยมโดยเฉพาะอย่างยิ่งการอ้างอิงถึง TryParse ซึ่งเป็นฟังก์ชัน (ทั่วไป) ซึ่งบังคับให้นักพัฒนาใช้ตัวแปร (ผิดปกติ) ออก
Cerebrus

12

มีเหตุผลหนึ่งในการใช้outพารามิเตอร์ที่ยังไม่ได้กล่าวถึง: วิธีการโทรมีหน้าที่ต้องรับมัน หากวิธีการของคุณสร้างค่าที่ผู้โทรไม่ควรละทิ้งทำให้เป็นoutแรงที่ผู้เรียกจะยอมรับโดยเฉพาะ:

 Method1();  // Return values can be discard quite easily, even accidentally

 int  resultCode;
 Method2(out resultCode);  // Out params are a little harder to ignore

แน่นอนผู้โทรยังคงเพิกเฉยต่อคุณค่าในoutพารามิเตอร์ แต่คุณได้เรียกความสนใจจากพวกเขา

นี่คือความต้องการที่หายาก; บ่อยครั้งคุณควรใช้ข้อยกเว้นสำหรับปัญหาของแท้หรือส่งคืนวัตถุที่มีข้อมูลสถานะสำหรับ "FYI" แต่อาจมีสถานการณ์ที่สิ่งนี้สำคัญ


8

มันชอบมาก

ฉันชอบผลตอบแทนและถ้าคุณมีผลตอบแทนหลายครั้งคุณสามารถสรุปได้ใน DTO ผลลัพธ์

public class Result{
  public Person Person {get;set;}
  public int Sum {get;set;}
}

5

คุณสามารถมีค่าส่งคืนได้หนึ่งค่าเท่านั้นในขณะที่คุณสามารถมีพารามิเตอร์ได้หลายค่า

คุณจะต้องพิจารณาพารามิเตอร์ในกรณีเหล่านั้นเท่านั้น

อย่างไรก็ตามหากคุณต้องการส่งคืนพารามิเตอร์มากกว่าหนึ่งพารามิเตอร์จากวิธีการของคุณคุณอาจต้องการดูสิ่งที่คุณส่งคืนจากวิธีการ OO และพิจารณาว่าคุณดีกว่าส่งคืนวัตถุหรือ struct ด้วยพารามิเตอร์เหล่านี้หรือไม่ ดังนั้นคุณจะกลับไปที่ค่าตอบแทนอีกครั้ง


5

คุณควรใช้ค่าส่งคืนเกือบทุกครั้ง ' out' พารามิเตอร์สร้างบิตของแรงเสียดทานมากของ API ที่ compositionality ฯลฯ

ข้อยกเว้นที่สำคัญที่สุดที่สปริงใจคือเมื่อคุณต้องการคืนค่าหลายค่า (.Net Framework ไม่มี tuples จนถึง 4.0) เช่นกับTryParseรูปแบบ


ไม่แน่ใจว่ามันเป็นแนวปฏิบัติที่ดี แต่ Arraylist สามารถใช้เพื่อคืนค่าหลายค่าได้
BA

@BA ไม่มันไม่ใช่วิธีปฏิบัติที่ดีผู้ใช้รายอื่นไม่รู้ว่า Arraylist นี้มีอะไรหรืออยู่ในตำแหน่งใด ตัวอย่างเช่นฉันต้องการคืนจำนวนไบต์ที่อ่านและค่า ออกหรือ tuples ซึ่งมีชื่อจะดีกว่ามาก ArrayList หรือรายการอื่น ๆ จะมีประโยชน์ก็ต่อเมื่อคุณต้องการส่งคืนการยุบของสิ่งต่าง ๆ เช่นรายชื่อบุคคล
sLw

2

ฉันต้องการสิ่งต่อไปนี้แทนที่จะเป็นตัวอย่างหนึ่งในตัวอย่างง่ายๆนี้

public int Value
{
    get;
    private set;
}

แต่พวกเขาทั้งหมดเหมือนกันมาก โดยปกติแล้วจะใช้เพียง 'ออก' หากพวกเขาต้องการที่จะผ่านหลายค่ากลับจากวิธีการ หากคุณต้องการที่จะส่งค่าเข้าและออกจากวิธีการหนึ่งจะเลือก 'อ้างอิง' วิธีการของฉันดีที่สุดถ้าคุณเพียงคืนค่า แต่ถ้าคุณต้องการส่งผ่านพารามิเตอร์และรับค่ากลับมาหนึ่งอาจจะเลือกตัวเลือกแรกของคุณ


2

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


2

นอกจากนี้ค่าส่งคืนเข้ากันได้กับกระบวนทัศน์การออกแบบแบบอะซิงโครนัส

คุณไม่สามารถกำหนดฟังก์ชั่น "async" ถ้ามันใช้พารามิเตอร์อ้างอิงหรือออก

โดยสรุปReturn Valuesอนุญาตให้มีการโยงเมธอดไวยากรณ์ที่สะอาดกว่า (โดยกำจัดความจำเป็นสำหรับผู้เรียกเพื่อประกาศตัวแปรเพิ่มเติม) และอนุญาตให้มีการออกแบบแบบอะซิงโครนัสโดยไม่จำเป็นต้องมีการปรับเปลี่ยนมากมายในอนาคต


จุดที่ดีในการกำหนด "async" คิดว่าคนอื่นจะพูดถึงเรื่องนั้น การผูกมัด - เช่นเดียวกับการใช้ค่าส่งคืน (แน่นอนฟังก์ชั่นของตัวเอง) เป็นนิพจน์เป็นกุญแจสำคัญอีกอย่างหนึ่ง เป็นความแตกต่างระหว่างการพิจารณาว่าจะเอาของกลับมาจากกระบวนการได้อย่างไร ("bucket" - Tuple / class / struct ฟังก์ชั่นของตัวเองเพราะมันส่งกลับเพียง 1 ค่า)
user1172173

1

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

หากคุณใช้ส่งคืนข้อมูลจะถูกเขียนลงในเมธอดสแต็กก่อนแล้วจึงใช้วิธีการโทร ในขณะที่ในกรณีที่ออกจะถูกเขียนโดยตรงไปยังกองวิธีการโทร ไม่แน่ใจว่ามีความแตกต่างอีกหรือไม่


กองวิธี? ฉันไม่ชำนาญ c # แต่ x86 รองรับเพียงหนึ่งสแต็คต่อเธรด "frame" ของเมธอดถูกยกเลิกการจัดสรรระหว่างการส่งคืนและหากการสลับบริบทเกิดขึ้นสแต็กที่จัดสรรคืนสามารถถูกเขียนทับได้ ในคทั้งหมดส่งคืนค่าไปในรีจิสทรี eax หากคุณต้องการคืนค่าวัตถุ / โครงสร้างพวกเขาจำเป็นต้องจัดสรรให้กับฮีปและตัวชี้จะถูกใส่ใน eax
Stefan Lundström

1

ดังที่คนอื่น ๆ ได้กล่าวไว้ว่ามูลค่าส่งคืนไม่ใช่มูลค่า

ฉันขอแนะนำหนังสือ "Framework Design Guidelines" ให้กับคุณได้หรือไม่ หน้า 184-185 ครอบคลุมถึงเหตุผลในการหลีกเลี่ยง params หนังสือทั้งเล่มจะนำคุณไปในทิศทางที่ถูกต้องเกี่ยวกับปัญหาการเข้ารหัสของ NET ทุกประเภท

แนวทางการออกแบบกรอบการทำงานคือการใช้เครื่องมือวิเคราะห์แบบคงที่คือ FxCop คุณจะพบสิ่งนี้ได้บนเว็บไซต์ของ Microsoft ว่าดาวน์โหลดได้ฟรี เรียกใช้สิ่งนี้กับโค้ดที่คอมไพล์แล้วและดูว่ามันเขียนอะไร ถ้ามันบ่นไปหลายร้อยเรื่อง ... อย่าตกใจ! ดูอย่างใจเย็นและรอบคอบในสิ่งที่พูดเกี่ยวกับแต่ละกรณี อย่ารีบเร่งแก้ไขสิ่งต่าง ๆ โดยเร็ว เรียนรู้จากสิ่งที่บอกคุณ คุณจะถูกนำตัวไปบนถนนเพื่อความเชี่ยวชาญ


1

การใช้คีย์เวิร์ด out พร้อมกับชนิดบูลคืนบางครั้งสามารถลดการขยายตัวของรหัสและเพิ่มความสามารถในการอ่าน (ส่วนใหญ่เมื่อข้อมูลเพิ่มเติมในพารามิเตอร์นอกมักจะถูกละเว้น) ตัวอย่างเช่น:

var result = DoThing();
if (result.Success)
{
    result = DoOtherThing()
    if (result.Success)
    {
        result = DoFinalThing()
        if (result.Success)
        {
            success = true;
        }
    }
}

VS:

var result;
if (DoThing(out result))
{
    if (DoOtherThing(out result))
    {
        if (DoFinalThing(out result))
        {
            success = true;
        }
    }
}

1

ไม่มีความแตกต่างที่แท้จริง พารามิเตอร์ Out อยู่ใน C # เพื่ออนุญาตให้เมธอดส่งคืนมากกว่าหนึ่งค่านั่นคือทั้งหมด

อย่างไรก็ตามมีความแตกต่างเล็กน้อย แต่ไม่ใช่ของพวกเขามีความสำคัญจริง ๆ :

การใช้พารามิเตอร์ out จะบังคับให้คุณใช้สองบรรทัดดังนี้:

int n;
GetValue(n);

ในขณะที่ใช้ค่าส่งคืนจะช่วยให้คุณทำมันในหนึ่งบรรทัด:

int n = GetValue();

ข้อแตกต่างอื่น (ถูกต้องเฉพาะกับประเภทของค่าและเฉพาะในกรณีที่ C # ไม่อินไลน์ฟังก์ชัน) คือการใช้ค่าส่งคืนจะต้องทำสำเนาของค่าเมื่อฟังก์ชันส่งคืนในขณะที่การใช้พารามิเตอร์ OUT จะไม่จำเป็นต้องทำเช่นนั้น


0

ออกมีประโยชน์มากขึ้นเมื่อคุณพยายามที่จะส่งคืนวัตถุที่คุณประกาศในวิธีการ

ตัวอย่าง

public BookList Find(string key)
{
   BookList book; //BookList is a model class
   _books.TryGetValue(key, out book) //_books is a concurrent dictionary
                                     //TryGetValue gets an item with matching key and returns it into book.
   return book;
}

0

ค่าส่งคืนเป็นปกติที่ส่งคืนโดยวิธีการของคุณ

โดยที่พารามิเตอร์out , well และ ref คือ 2 คำสำคัญของ C # ที่อนุญาตให้ส่งผ่านตัวแปรเป็นการอ้างอิงอ้างอิง

ความแตกต่างใหญ่ระหว่างโทษและออกมามีเตะควรจะเริ่มต้นใช้ก่อนและออกมาไม่ได้


-2

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

ฉันเชื่อว่ามันเหมาะกับภาษาการเขียนโปรแกรมเชิงวัตถุที่ดีกว่าสำหรับขั้นตอนการคืนค่า (VRP) ของพวกเขาเพื่อกำหนดและบริสุทธิ์

'VRP' เป็นชื่อทางวิชาการที่ทันสมัยสำหรับฟังก์ชั่นที่ถูกเรียกว่าเป็นส่วนหนึ่งของการแสดงออกและมีค่าตอบแทนที่แทนที่การเรียกในระหว่างการประเมินการแสดงออก เช่นในคำสั่งเช่นx = 1 + f(y)ฟังก์ชันfกำลังทำหน้าที่เป็น VRP

'กำหนด' หมายความว่าผลลัพธ์ของฟังก์ชันขึ้นอยู่กับค่าของพารามิเตอร์เท่านั้น หากคุณเรียกมันอีกครั้งด้วยค่าพารามิเตอร์เดียวกันคุณจะได้รับผลลัพธ์ที่เหมือนกัน

'บริสุทธิ์' หมายถึงไม่มีผลข้างเคียง: การเรียกใช้ฟังก์ชันไม่ได้ทำอะไรนอกจากการคำนวณผลลัพธ์ สิ่งนี้สามารถตีความได้ว่าไม่มีความสำคัญผลข้างเคียงที่ในทางปฏิบัติดังนั้นหาก VRP แสดงข้อความดีบั๊กทุกครั้งที่มีการเรียกใช้ตัวอย่างเช่นนั่นอาจจะถูกละเว้น

ดังนั้นหากใน C #, ฟังก์ชั่นของคุณไม่ได้กำหนดและบริสุทธิ์ผมบอกว่าคุณควรจะทำให้มันเป็นvoidฟังก์ชั่น (ในคำอื่น ๆ ที่ไม่ได้เป็น VRP) และค่าใด ๆ จะต้องมีผลตอบแทนที่ควรจะกลับมาในไม่ว่าจะเป็นoutหรือrefพารามิเตอร์

ตัวอย่างเช่นหากคุณมีฟังก์ชั่นที่จะลบบางแถวออกจากตารางฐานข้อมูลและคุณต้องการให้มันคืนจำนวนแถวที่ลบไปคุณควรประกาศให้มันเป็นอย่างนี้:

public void DeleteBasketItems(BasketItemCategory category, out int count);

หากบางครั้งคุณต้องการเรียกใช้ฟังก์ชั่นนี้ แต่ไม่ได้รับcountคุณสามารถประกาศการโหลดมากเกินไปได้

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

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

ข้อเสียของการเขียนโปรแกรมเชิงโพรซีเดอร์คือเนื่องจากคุณสามารถรันโค้ดโดยพลการได้ทุกที่คุณสามารถรับการโต้ตอบที่ไม่พึงประสงค์และมีจุดอ่อนได้ผ่านตัวแปรทั่วโลกและผลข้างเคียง

ดังนั้นค่อนข้างง่ายมันเป็นการดีที่จะส่งสัญญาณให้คนที่อ่านโค้ดของคุณฟังก์ชั่นอาจมีผลข้างเคียงโดยทำให้มันกลับมาโดยไม่มีค่า


> หากคุณต้องการเรียกใช้ฟังก์ชั่นนี้ในบางครั้ง แต่ไม่ได้รับการนับคุณสามารถประกาศการโอเวอร์โหลดได้เสมอ ใน C # เวอร์ชัน 7 (ฉันคิดว่า) และใหม่กว่าคุณสามารถใช้_สัญลักษณ์ทิ้งเพื่อละเว้นพารามิเตอร์ out เช่น DeleteBasketItems (หมวดหมู่ออก _);
อภิปราย
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.