ฉันจะเริ่มต้นพารามิเตอร์เป็น Guid.Empty ใน C # ได้อย่างไร


178

ฉันอยากจะบอกว่า:

public void Problem(Guid optional = Guid.Empty)
{
}

แต่คอมไพเลอร์บ่นว่า Guid.Empty ไม่ใช่ค่าคงที่เวลารวบรวม

เนื่องจากฉันไม่ต้องการเปลี่ยน API ฉันไม่สามารถใช้:

 Nullable<Guid>


เกิดอะไรขึ้นกับการเปลี่ยนไปใช้Nullable<Guid> optional = null(หรือมากกว่านั้นGuid? optional = null) Guid ใด ๆ ที่ส่งผ่านไปยังมันจะถูกบีบบังคับโดยไม่ต้องมีการเปลี่ยนแปลงรหัสใด ๆ เลย
นิวแฮมป์เชียร์

คำตอบ:


235

สารละลาย

คุณสามารถใช้new Guid()แทน

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

คุณยังสามารถใช้ default(Guid)

default(Guid)new Guid()ยังจะทำงานตรงตามที่

เนื่องจาก Guid เป็นประเภทค่าไม่ใช่ชนิดอ้างอิงดังนั้นจึงdefault(Guid)ไม่เท่ากับnullตัวอย่างแทนมันเท่ากับการเรียก constructor เริ่มต้น

ซึ่งหมายความว่า:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

มันเหมือนกับตัวอย่างดั้งเดิม

คำอธิบาย

ทำไมไม่Guid.Emptyทำงาน

เหตุผลที่คุณได้รับข้อผิดพลาดคือเนื่องจากEmptyมีการกำหนดเป็น:

public static readonly Guid Empty;

ดังนั้นมันจึงเป็นตัวแปรไม่ใช่ค่าคงที่ (กำหนดเป็นstatic readonlyไม่เป็นconst) คอมไพเลอร์สามารถมีค่าที่ทราบได้เฉพาะคอมไพเลอร์เป็นค่าเริ่มต้นของพารามิเตอร์เมธอด (ไม่ใช่รู้จักเฉพาะรันไทม์)

สาเหตุคือการที่คุณไม่สามารถมีconstใด ๆstructซึ่งแตกต่างจากenumตัวอย่างเช่น หากคุณลองมันจะไม่รวบรวม

เหตุผลอีกครั้งstructคือไม่ได้เป็นประเภทดั้งเดิม
สำหรับรายการประเภทดั้งเดิมทั้งหมดใน. NET ดูhttp://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(โปรดทราบว่าenumโดยปกติแล้วจะสืบทอดintซึ่งเป็นแบบดั้งเดิม)

แต่new Guid()ก็ไม่คงที่เช่นกัน!

ฉันไม่ได้บอกว่ามันต้องการค่าคงที่ มันต้องการสิ่งที่สามารถตัดสินใจได้ในเวลารวบรวม Emptyเป็นเขตข้อมูลดังนั้นจึงไม่ทราบค่าในเวลารวบรวม (ในช่วงเริ่มต้นเท่านั้น)

ค่าพารามิเตอร์เริ่มต้นจะต้องรู้จักกันในเวลารวบรวมซึ่งอาจเป็น constค่าหรือสิ่งที่กำหนดโดยใช้คุณสมบัติ C # ที่ทำให้ค่าที่รู้จักกันในเวลารวบรวมเช่นdefault(Guid)หรือnew Guid()(ซึ่งจะตัดสินใจในเวลารวบรวมสำหรับstructs ในขณะที่คุณไม่สามารถแก้ไขตัวstructสร้างใน รหัส).

ในขณะที่คุณสามารถให้defaultหรือnewง่ายคุณไม่สามารถให้const(เพราะไม่ใช่ประเภทดั้งเดิมหรือenumตามที่อธิบายไว้ข้างต้น) ดังนั้นอีกครั้งไม่ได้บอกว่าตัวเลือกพารามิเตอร์ตัวเองต้องการค่าคงที่ แต่รวบรวมค่าที่รู้จักกัน


5
มันไม่จำเป็นต้องมีการแสดงออกคงที่ปกติ - new Guid()ไม่ใช่การแสดงออกอย่างคงที่ ข้อมูลจำเพาะ C # กำหนดค่อนข้างชัดเจนว่าอะไรที่อนุญาตรวมถึงแต่ไม่ จำกัด เฉพาะค่าคงที่ (เพื่อให้ชัดเจนมันมีประสิทธิภาพเป็นค่าคงที่เวลาคอมไพล์ไม่ใช่ "นิพจน์ค่าคงที่" ในข้อกำหนดข้อกำหนด C #)
Jon Skeet

3
อ่านส่วนคำอธิบายในคำตอบของฉัน เพิ่มเพื่อตอบส่วนนี้
Meligy

คำอธิบายดีขอบคุณ!
Daryl

คุณสามารถใช้ได้เฉพาะdefaultในทุกวันนี้ :)
Joshit

151

Guid.Emptyเทียบเท่ากับซึ่งเทียบเท่ากับnew Guid() default(Guid)ดังนั้นคุณสามารถใช้:

public void Problem(Guid optional = default(Guid))

หรือ

public void Problem(Guid optional = new Guid())

โปรดทราบว่าnew Foo()ค่าจะใช้ได้เฉพาะเมื่อ:

  • คุณกำลังเรียกตัวสร้างแบบไร้พารามิเตอร์
  • Foo เป็นประเภทค่า

กล่าวอีกนัยหนึ่งเมื่อคอมไพเลอร์รู้ว่ามันเป็นเพียงค่าเริ่มต้นสำหรับประเภท :)

(น่าสนใจฉัน 99.9% แน่ใจว่าจะไม่เรียกตัวnew Foo()สร้างแบบกำหนดเองใด ๆ ที่คุณอาจสร้างขึ้นคุณไม่สามารถสร้างตัวสร้างเช่นนี้ในประเภทค่าใน C # แต่คุณสามารถทำได้ใน IL)

คุณสามารถใช้default(Foo)ตัวเลือกสำหรับประเภทใดก็ได้


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

4
@Ian Ringrose: ฉันไม่คิดว่าคอมไพเลอร์ควรมีข้อความเฉพาะประเภทโดยทั่วไปเพื่อความซื่อสัตย์
Jon Skeet

2
การตั้งค่าพารามิเตอร์เป็นค่าเริ่มต้นเป็นวัตถุใหม่จะสร้างวัตถุใหม่ทุกครั้งที่มีการเรียกใช้เมธอดใน PHP แต่สร้างเพียงหนึ่งวัตถุสำหรับโปรแกรมทั้งหมดใน Python จริงๆผมพิจารณานี้จะเป็นหนึ่งในข้อบกพร่องของการออกแบบที่น้อยมากของงูใหญ่ ฉันค่อนข้างดีใจที่ C # (และ VB.Net) หลีกเลี่ยงปัญหานี้โดยการไม่อนุญาตวัตถุใหม่ในพารามิเตอร์เริ่มต้น ... แม้ว่าจะมีบางครั้งที่ความสามารถนี้ดีมากใน PHP
BlueRaja - Danny Pflughoeft

1
สิ่งที่อาจเป็นสาเหตุที่สิ่งเดียวกันนี้ไม่ทำงานในการดำเนินการควบคุม ASP.NET MVC? พารามิเตอร์ทางเลือกอื่น ๆ ทั้งหมดใช้งานได้ (int, สตริง) แต่ใช้งานไม่ได้กับ GUID กล่าวว่า "เซิร์ฟเวอร์เกิดข้อผิดพลาดใน '/' แอปพลิเคชันพจนานุกรมพารามิเตอร์มีรายการที่เป็นโมฆะสำหรับพารามิเตอร์ 'categoryId' '.. "โค้ดคอมไพล์ได้ดีกับข้อกำหนดทั้งสอง (ค่าเริ่มต้น (Guid) และกับ Guid ใหม่ ()) แต่ออกข้อผิดพลาดนี้
mare

@ Mare: ฉันไม่รู้ฉันกลัว ตัวเลือกอื่นจะใช้Nullable<Guid>อาจ
Jon Skeet

18

คุณไม่สามารถใช้:

default ( Guid ) ?


1
ไม่ได้ Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
เรียกซ้ำเมื่อ

4
ขออภัยฉันไม่ได้ตั้งใจ ในฐานะผู้ดำเนินการ แต่เป็นเครื่องหมายคำถามที่เน้น - ฉันจะแก้ไข!
Nick

9

คำตอบที่ยอมรับไม่ทำงานใน ASP.NET MVC และทำให้เกิดข้อผิดพลาดขณะทำงานนี้:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

คุณสามารถทำสิ่งต่อไปนี้แทน:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}

ฉันเห็นเหตุผลของการลงคะแนน: คำถามระบุว่า API ไม่สามารถเปลี่ยนเป็นใช้ Nullable <Guid> - ยุติธรรมพอ
Majix

หากคุณไม่ต้องการกำหนดพารามิเตอร์ "ทางเลือก" อย่างชัดเจนด้วยค่า Guid ที่ว่างเปล่าฉันคิดว่านี่เป็นวิธีที่เป็นธรรมชาติที่สุดในการกำหนดพารามิเตอร์ทางเลือกประเภท Guid
กอนซาโล่

4

คอมไพเลอร์ค่อนข้างถูกต้อง Guid.Emptyไม่ใช่ค่าคงที่เวลารวบรวม คุณสามารถลองสร้างวิธีการโอเวอร์โหลดดังนี้:

public void Problem()
{
    Problem(Guid.Empty);
}

ฉันบอกว่าฉันไม่ต้องการเปลี่ยน API วิธีที่ฉันพยายามทำให้เชื่องได้เกิน 10 parms!
Ian Ringrose

@Ian Ringrose แม้ว่าฉันจะเห็นด้วยกับGuid x = default(Guid)วิธีการแก้ปัญหาโปรดจำไว้ว่าการเพิ่มฟังก์ชั่นเกินพิกัดอื่นไม่ซับซ้อน API มากกว่าการเพิ่มอาร์กิวเมนต์ตัวเลือก นั่นคือสิ่งที่เป็นข้อโต้แย้งทางเลือก
สิบสี่

@ อีกสี่มันจะต้องมีฟังก์ชั่นใหม่มากมายที่จะทำเช่นเดียวกับ 10 parms เสริม!
Ian Ringrose

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

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