คุณจะใช้พารามิเตอร์ทางเลือกใน C # ได้อย่างไร?


493

หมายเหตุ:คำถามนี้ถูกถามในเวลาที่ C # ยังไม่สนับสนุนพารามิเตอร์ทางเลือก (เช่นก่อนหน้า C # 4)

เรากำลังสร้างเว็บ API ที่สร้างโดยทางโปรแกรมจากคลาส C # ชั้นมีวิธีการGetFooBar(int a, int b)และ API มีวิธีGetFooBarการ params &a=foo &b=barแบบสอบถามเหมือน

คลาสจำเป็นต้องสนับสนุนพารามิเตอร์ทางเลือกซึ่งไม่ได้รับการสนับสนุนในภาษา C # วิธีที่ดีที่สุดคืออะไร


7
หรือรอยูนิตที่ปล่อย C # 4.0 รองรับพารามิเตอร์เสริม
คาห์

คำตอบ:


1055

แปลกใจที่ไม่มีใครพูดถึงพารามิเตอร์ทางเลือก C # 4.0 ที่ทำงานเช่นนี้:

public void SomeMethod(int a, int b = 0)
{
   //some code
}

แก้ไข:ฉันรู้ว่าในเวลาที่ถามคำถาม C # 4.0 ไม่มีอยู่จริง แต่คำถามนี้ยังคงอยู่ในอันดับที่ 1 ใน Google สำหรับ "อาร์กิวเมนต์เพิ่มเติมที่เป็นตัวเลือก C #" ดังนั้นฉันจึงคิดว่า - คำตอบนี้น่าจะอยู่ที่นี่ ขอโทษ


58
ในขณะที่คำถามถูกถาม C # 4.0 ไม่มีอยู่จริง
Forrest Marvez

91
ใช่มองข้ามสิ่งนี้ขอโทษ แต่คำถามนี้ยังคงจัดอันดับ 1 ใน Google สำหรับ "ข้อโต้แย้งตัวเลือก C #" ดังนั้นต่อไป - นี้คุ้มค่าคำตอบอยู่ที่นี่ :)
อเล็กซ์

45
ดีสำหรับคุณในการให้ข้อมูลที่ทันสมัย โดยทั่วไปแล้วคำตอบดั้งเดิมจะได้รับการปรับปรุงด้วยข้อมูลปัจจุบันเช่น C # 4.0 ฉันเชื่อว่านั่นคือสิ่งที่ผู้ชายในตอนแรกมีอยู่ในใจความคิดของ Wiki แต่ทุกคนกลัวเกินไปที่จะแก้ไขคำตอบของคนอื่น
Rowan

สิ่งที่น่าสนใจที่ฉันค้นพบกับ WebServices ก็คือ (ในโครงการที่ฉันกำลังทดสอบ) ค่าบูลทั้งหมดในการสืบค้นเป็นทางเลือกโดยปริยาย เห็นได้ชัดว่าพวกเขาเริ่มต้นเป็น FALSE
Carlos P

4
เป็นที่น่าสังเกตว่าจะต้องวางพารามิเตอร์ทางเลือกหลังจากพารามิเตอร์ที่ไม่จำเป็นทั้งหมด เช่นคุณไม่สามารถมีโมฆะสาธารณะบางวิธี (int b = 0, int a)
Andrew Meservy

129

ตัวเลือกอื่นคือการใช้คำหลักพารามิเตอร์

public void DoSomething(params object[] theObjects)
{
  foreach(object o in theObjects)
  {
    // Something with the Objects…
  }
}

เรียกว่าเหมือน ...

DoSomething(this, that, theOther);

57
คำตอบนี้จะอธิบายคำสำคัญของพารามิเตอร์ที่สวยงามใน 10 บรรทัด MSDN ยังคงไม่สามารถทำได้ในวันที่ 1 +1
User2400

1
ขอบคุณ! นี่เป็นแรงบันดาลใจให้ฉันเขียนฟังก์ชั่นการบันทึกที่เรียบง่าย (สำหรับกรณีการใช้งานปัจจุบันของฉัน): public void log (params object[] args){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.Length; i++){ sb.Append("{"); sb.Append(i.ToString()); sb.Append("}"); sb.Append(" "); } String.Format(sb.ToString(),args).Dump(); }การโทรตัวอย่าง:log("...Done,",(watch.ElapsedMilliseconds/1000).ToString(),"s");
pfonseca

คุณสามารถพูดถึง net 3.5 รองรับหรือไม่ เอกสารอย่างเป็นทางการไม่ได้พูดถึงว่า
T.Todua

@ T.Todua - ฉันไม่แน่ใจ 100% ว่าคุณขออะไรอยู่ที่นี่ - dotnet framework 3.5 รวม C # v3.0 - เวอร์ชัน 3.0 สนับสนุนคำสำคัญ params แต่ไม่มีพารามิเตอร์ทางเลือกซึ่งถูกนำมาใช้ใน C # v4.0 ซึ่งรวมอยู่ใน dotnet framework 4.0
Tim Jarvis

@ TimJarvis ใช่นั่นคือฉันกำลังขอ tnx
T.Todua

77

ใน C # ปกติฉันจะใช้วิธีการหลายรูปแบบ:

void GetFooBar(int a) { int defaultBValue;  GetFooBar(a, defaultBValue); }
void GetFooBar(int a, int b)
{
 // whatever here
}

UPDATE: สิ่งที่กล่าวมาข้างต้นนี้เป็นวิธีที่ฉันทำกับค่าเริ่มต้นด้วย C # 2.0 โครงการที่ฉันกำลังทำอยู่ตอนนี้ใช้ C # 4.0 ซึ่งตอนนี้สนับสนุนพารามิเตอร์ทางเลือกโดยตรง นี่คือตัวอย่างที่ฉันเพิ่งใช้ในรหัสของตัวเอง:

public EDIDocument ApplyEDIEnvelop(EDIVanInfo sender, 
                                   EDIVanInfo receiver, 
                                   EDIDocumentInfo info,
                                   EDIDocumentType type 
                                     = new EDIDocumentType(EDIDocTypes.X12_814),
                                   bool Production = false)
{
   // My code is here
}

1
ฉันเชื่อว่าคุณไม่สามารถเรียก constructors ในพารามิเตอร์เริ่มต้นพวกเขาจำเป็นต้อง "คอมไพล์เวลา" เข้ากันได้ไม่ใช่ "รันไทม์" ... หรือฉันผิด
Alex

45

จากเว็บไซต์นี้:

http://www.tek-tips.com/viewthread.cfm?qid=1500861&page=1

C # อนุญาตให้ใช้แอตทริบิวต์ [ไม่บังคับ] (จาก VB แม้ว่าจะไม่สามารถใช้งานได้ใน C #) ดังนั้นคุณสามารถมีวิธีดังนี้:

using System.Runtime.InteropServices;
public void Foo(int a, int b, [Optional] int c)
{
  ...
}

ใน wrapper API ของเราเราตรวจหาพารามิเตอร์ที่เป็นตัวเลือก (ParameterInfo p.IsOptional) และตั้งค่าเริ่มต้น เป้าหมายคือการทำเครื่องหมายพารามิเตอร์เป็นทางเลือกโดยไม่ต้องหันไปใช้ kludges เช่นมี "ทางเลือก" ในชื่อพารามิเตอร์


4
แต่จะใช้ฟังก์ชั่น Foo ได้อย่างไร? Foo (1,1); foo (1,1, null) และ foo (1,1, Missing.value) ทั้งหมดจะโยนข้อยกเว้น
Bolu

2
แปลกคำตอบข้างต้นดีกว่านี้มาก
Maidot

26

คุณสามารถใช้วิธีการมากไป ...

GetFooBar ()
GetFooBar (int a)
GetFooBar (int a, int b)

มันขึ้นอยู่กับวิธีลายเซ็นตัวอย่างที่ฉันให้นั้นไม่มีเมธอด "int b" เท่านั้นเพราะมันจะมีลายเซ็นเหมือนกันกับวิธี "int a"

คุณสามารถใช้ประเภท Nullable ...

GetFooBar (int? a, int? b)

จากนั้นคุณสามารถตรวจสอบโดยใช้ a.HasValue เพื่อดูว่ามีการตั้งค่าพารามิเตอร์ไว้หรือไม่

ตัวเลือกอื่นจะใช้พารามิเตอร์ 'params'

GetFooBar (วัตถุ params [] args)

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


24

คุณสามารถใช้พารามิเตอร์ทางเลือกใน C # 4.0 ได้โดยไม่ต้องกังวล หากเรามีวิธีการเช่น:

int MyMetod(int param1, int param2, int param3=10, int param4=20){....}

เมื่อคุณเรียกเมธอดคุณสามารถข้ามพารามิเตอร์ดังนี้:

int variab = MyMethod(param3:50; param1:10);

C # 4.0 ใช้คุณสมบัติที่เรียกว่า "named พารามิเตอร์" คุณสามารถส่งผ่านพารามิเตอร์ตามชื่อของพวกเขาได้และแน่นอนว่าคุณสามารถส่งผ่านพารามิเตอร์ตามลำดับที่คุณต้องการ :)


20

สวัสดีโลกที่เป็นทางเลือก

หากคุณต้องการให้รันไทม์จัดหาค่าพารามิเตอร์เริ่มต้นคุณต้องใช้การสะท้อนกลับเพื่อโทรออก ไม่ดีเท่ากับคำแนะนำอื่น ๆ สำหรับคำถามนี้ แต่เข้ากันได้กับ VB.NET

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace ConsoleApplication1
{
    class Class1
    {
        public static void sayHelloTo(
            [Optional,
            DefaultParameterValue("world")] string whom)
        {
            Console.WriteLine("Hello " + whom);
        }

        [STAThread]
        static void Main(string[] args)
        {
            MethodInfo mi = typeof(Class1).GetMethod("sayHelloTo");
            mi.Invoke(null, new Object[] { Missing.Value });
        }
    }
}

16

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

public void PrintValues(int? a = null, int? b = null, float? c = null, string s = "")
{
    if(a.HasValue)
        Console.Write(a);
    else
        Console.Write("-");

    if(b.HasValue)
        Console.Write(b);
    else
        Console.Write("-");

    if(c.HasValue)
        Console.Write(c);
    else
        Console.Write("-");

    if(string.IsNullOrEmpty(s)) // Different check for strings
        Console.Write(s);
    else
        Console.Write("-");
}

สตริงเป็นประเภท nullable อยู่แล้วดังนั้นพวกเขาจึงไม่ต้องการ? .

เมื่อคุณมีวิธีนี้การโทรต่อไปนี้จะใช้ได้ทั้งหมด :

PrintValues (1, 2, 2.2f);
PrintValues (1, c: 1.2f);
PrintValues(b:100);
PrintValues (c: 1.2f, s: "hello");
PrintValues();

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

อาร์กิวเมนต์ที่ระบุชื่อและไม่บังคับ (คู่มือการเขียนโปรแกรม C #) @ MSDN


9

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

public void Foo(int a, int b, int? c)
{
  if(c.HasValue)
  {
    // do something with a,b and c
  }
  else
  {
    // do something with a and b only
  }  
}

+1 คำแนะนำ อย่าทำสิ่งนี้ให้เป็นนิสัยเพราะอาจทำให้รกได้
mhenrixon

7

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

  • ใช้ c # 4.0: ใช้อาร์กิวเมนต์ที่เป็นตัวเลือกในตัวสร้างคลาสซึ่งเป็นโซลูชันที่ฉันชอบเนื่องจากใกล้กับสิ่งที่ทำด้วยวิธีการมากขึ้นดังนั้นจึงง่ายต่อการจดจำ นี่คือตัวอย่าง:

    class myClass
    {
        public myClass(int myInt = 1, string myString =
                               "wow, this is cool: i can have a default string")
        {
            // do something here if needed
        }
    }
  • ใช้รุ่น c # ก่อนหน้า c # 4.0: คุณควรใช้การกำหนดสายงานคอนสตรัคชัน (ใช้: คำหลักนี้) ซึ่งตัวสร้างที่เรียบง่ายนำไปสู่ ​​"ตัวสร้างหลัก" ตัวอย่าง:

    class myClass
    {
        public myClass()
        {
        // this is the default constructor
        }
    
        public myClass(int myInt)
            : this(myInt, "whatever")
        {
            // do something here if needed
        }
        public myClass(string myString)
            : this(0, myString)
        {
            // do something here if needed
        }
        public myClass(int myInt, string myString)
        {
            // do something here if needed - this is the master constructor
        }
    }

3

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


2

คุณสามารถโอเวอร์โหลดวิธีการของคุณ วิธีหนึ่งมีหนึ่งพารามิเตอร์GetFooBar(int a)และอีกวิธีหนึ่งมีทั้งพารามิเตอร์GetFooBar(int a, int b)


2

การใช้งานเกินพิกัดหรือใช้ C # 4.0 หรือสูงกว่า

 private void GetVal(string sName, int sRoll)
 {
   if (sRoll > 0)
   {
    // do some work
   }
 }

 private void GetVal(string sName)
 {
    GetVal("testing", 0);
 }

1

สำหรับพารามิเตอร์ที่เป็นทางเลือกจำนวนมากขึ้นสามารถใช้พารามิเตอร์เดียวของ Dictionary กับวิธีการของ containkey ฉันชอบวิธีนี้เพราะมันทำให้ฉันสามารถส่ง List หรือ T ทีละรายการโดยไม่ต้องสร้างวิธีอื่นทั้งหมด (ดีถ้าพารามิเตอร์ถูกใช้เป็นตัวกรองตัวอย่างเช่น)

ตัวอย่าง (พจนานุกรมใหม่ <สตริง, วัตถุ> () จะถูกส่งผ่านหากไม่ต้องการพารามิเตอร์ทางเลือก):

public bool Method(string ParamA, Dictionary<string,Object> AddlParams) {
    if(ParamA == "Alpha" && (AddlParams.ContainsKey("foo") || AddlParams.ContainsKey("bar"))) {
        return true;
    } else {
        return false;
    }
}

0

แทนที่จะเป็นพารามิเตอร์เริ่มต้นทำไมไม่เพียงสร้างคลาสพจนานุกรมจากการสอบถามผ่านแล้วการใช้งานที่เกือบจะเหมือนกับวิธีที่ asp.net ฟอร์มทำงานกับการสอบถาม

เช่น Request.QueryString ["a"]

นี่จะเป็นการแยกคลาสใบไม้จากรหัสโรงงาน / หม้อไอน้ำ


นอกจากนี้คุณยังอาจต้องการตรวจสอบWeb Services ด้วย ASP.NET บริการเว็บเป็นเว็บ API ที่สร้างขึ้นโดยอัตโนมัติผ่านแอตทริบิวต์ในคลาส C #


0

สายไปงานเลี้ยงเล็ก ๆ น้อย ๆ แต่ฉันกำลังมองหาคำตอบสำหรับคำถามนี้และในที่สุดก็หาวิธีอื่นในการทำเช่นนี้ ประกาศชนิดข้อมูลสำหรับ args เผื่อเลือกของวิธีการเว็บของคุณให้เป็นประเภท XmlNode หากไม่ใส่ arg ตัวเลือกนี้จะถูกตั้งค่าเป็นโมฆะและหากมีอยู่คุณจะได้รับคือค่าสตริงโดยการเรียก arg.Value เช่น

[WebMethod]
public string Foo(string arg1, XmlNode optarg2)
{
    string arg2 = "";
    if (optarg2 != null)
    {
        arg2 = optarg2.Value;
    }
    ... etc
}

สิ่งที่ดีเกี่ยวกับวิธีการนี้คือ. NET สร้างโฮมเพจสำหรับ ws ยังคงแสดงรายการอาร์กิวเมนต์ (แม้ว่าคุณจะสูญเสียช่องป้อนข้อความที่มีประโยชน์สำหรับการทดสอบ)


3
สิ่งนี้ดีกว่าการใช้ประเภท nullable หรือไม่
Kirk Broadhurst

0

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

method1 (param1, param2, param 3, param 4, param 5, param 6, param7) method1 (param1, param2, param3, param3, param3, param5, param3) วิธีที่ 1 (param1, param2, param4, param3, param5 ) ... เริ่มเห็นภาพ วิธีนี้อยู่ที่ความบ้าคลั่ง รวมกันมากเกินไป

ตอนนี้สำหรับวิธีที่ง่ายกว่าที่ดูน่าอึดอัดใจ แต่ควรใช้งานได้: method1 (param1, bool useParam1, param2, bool useParam2, ฯลฯ ... )

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

มันแฮ็ค แต่มันจะใช้ได้


2
นี่คือสาเหตุที่มีพารามิเตอร์ที่ไม่มีค่าได้
Kirk Broadhurst

0

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


0

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

พารามิเตอร์ตัวเลือกโทรกลับ:

public static bool IsOnlyOneElement(this IList lst, Action callbackOnTrue = (Action)((null)), Action callbackOnFalse = (Action)((null)))
{
    var isOnlyOne = lst.Count == 1;
    if (isOnlyOne && callbackOnTrue != null) callbackOnTrue();
    if (!isOnlyOne && callbackOnFalse != null) callbackOnFalse();
    return isOnlyOne;
}

0

พารามิเตอร์ทางเลือกคืออะไร แต่พารามิเตอร์เริ่มต้น! ฉันขอแนะนำให้คุณระบุพารามิเตอร์เริ่มต้นทั้งคู่ GetFooBar (int a = 0, int b = 0) หากคุณไม่มีวิธี overloaded ใด ๆ จะส่งผลให้ = 0, b = 0 หากคุณไม่ผ่านค่าใด ๆ หากคุณผ่าน 1 ค่าจะส่งผลให้ ค่าที่ส่งผ่านสำหรับ a, 0 และถ้าคุณผ่าน 2 ค่าที่ 1 จะถูกกำหนดให้กับ a และวินาทีถึง b

หวังว่าจะตอบคำถามของคุณ


0

ในกรณีที่ไม่มีค่าเริ่มต้นวิธีการเพิ่มพารามิเตอร์ทางเลือกคือการใช้. NET classAttribute ชั้น - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ดู = netframework-4.8

ตัวอย่างของรหัสด้านล่าง:

namespace OptionalParameterWithOptionalAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            //Calling the helper method Hello only with required parameters
            Hello("Vardenis", "Pavardenis");
            //Calling the helper method Hello with required and optional parameters
            Hello("Vardenis", "Pavardenis", "Palanga");
        }
        public static void Hello(string firstName, string secondName, 
            [System.Runtime.InteropServices.OptionalAttribute] string  fromCity)
        {
            string result = firstName + " " + secondName;
            if (fromCity != null)
            {
                result += " from " + fromCity;
            }
            Console.WriteLine("Hello " + result);
        }

    }
}

-4

คุณสามารถลอง
พิมพ์ Type 1 ได้เช่นกัน
public void YourMethod(int a=0, int b = 0) { //some code }


ประเภทที่ 2
public void YourMethod(int? a, int? b) { //some code }

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