Làm cách nào tôi có thể mặc định một tham số cho Guid.Empty trong C #?


178

Tôi muốn nói:

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

Nhưng trình biên dịch phàn nàn rằng Guid.Empty không phải là hằng số thời gian biên dịch.

Vì tôi không muốn thay đổi API nên tôi không thể sử dụng:

 Nullable<Guid>


Điều gì là sai khi chuyển sang Nullable<Guid> optional = null(hoặc chặt chẽ hơn, Guid? optional = null)? Bất kỳ Hướng dẫn nào hiện được truyền cho nó sẽ ép buộc mà không có bất kỳ thay đổi mã nào cần thiết cả.
NH.

Câu trả lời:


235

Giải pháp

Bạn có thể sử dụng new Guid()thay thế

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

Bạn cũng có thể dùng default(Guid)

default(Guid)cũng sẽ làm việc chính xác như new Guid().

Vì Guid là loại giá trị không phải là loại tham chiếu, do đó, default(Guid)không bằng nullvới ví dụ, thay vào đó, nó bằng với việc gọi hàm tạo mặc định.

Điều đó có nghĩa là điều này:

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

Nó chính xác giống như ví dụ ban đầu.

Giải trình

Tại sao không Guid.Emptylàm việc?

Lý do bạn nhận được lỗi là vì Emptyđược định nghĩa là:

public static readonly Guid Empty;

Vì vậy, nó là một biến, không phải là hằng số (được định nghĩa là static readonlykhông phải const). Trình biên dịch chỉ có thể có các giá trị được biết đến của trình biên dịch làm các giá trị mặc định của tham số phương thức (không phải chỉ biết về thời gian chạy).

Nguyên nhân sâu xa là bạn không thể có constbất kỳ nguyên nhân nào struct, không giống enumnhư. Nếu bạn thử nó, nó sẽ không biên dịch.

Lý do một lần nữa là đó structkhông phải là một loại nguyên thủy.
Để biết danh sách tất cả các loại nguyên thủy trong .NET, hãy xem http://msdn.microsoft.com/en-gb/l Library / system.typecode.aspx
(lưu ý enumthường là kế thừa int, đó là một nguyên thủy)

Nhưng new Guid()không phải là một hằng số quá!

Tôi không nói rằng nó cần một hằng số. Nó cần một cái gì đó có thể được quyết định trong thời gian biên dịch. Emptylà một trường, vì vậy, giá trị của nó không được biết đến trong thời gian biên dịch (chỉ khi bắt đầu thời gian chạy).

Giá trị tham số mặc định phải được biết tại thời gian biên dịch, có thể là constgiá trị hoặc được xác định bằng cách sử dụng tính năng C # để tạo giá trị được biết tại thời gian biên dịch, như default(Guid)hoặc new Guid()(được quyết định tại thời gian biên dịch cho structs khi bạn không thể sửa đổi hàm structtạo trong mã).

Mặc dù bạn có thể cung cấp defaulthoặc newdễ dàng, bạn không thể cung cấp const(vì đó không phải là loại nguyên thủy hoặc enumnhư đã giải thích ở trên). Vì vậy, một lần nữa, không nói rằng chính tham số tùy chọn cần một giá trị không đổi, nhưng trình biên dịch đã biết.


5
Chà, chẳng cần phải có biểu thức hằng bình thường - new Guid()chẳng hạn, không phải là biểu thức hằng. Thông số kỹ thuật C # xác định khá rõ ràng những gì được phép, bao gồm nhưng không giới hạn ở các hằng số. (Nói rõ hơn, nó thực sự một hằng số thời gian biên dịch, chỉ không phải là "biểu thức hằng" trong thuật ngữ C # spec).
Jon Skeet

3
Đọc phần Giải thích trong bài trả lời của tôi. Thêm vào để trả lời phần này.
Meligy

Lời giải thích tốt đẹp. Cảm ơn!
Daryl

Bạn chỉ có thể sử dụng ngay defaulthôm nay :)
Joshit

151

Guid.Emptytương đương với new Guid(), tương đương với default(Guid). Vì vậy, bạn có thể sử dụng:

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

hoặc là

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

Lưu ý rằng new Foo()giá trị chỉ được áp dụng khi:

  • Bạn đang thực sự gọi parameterless constructor
  • Foo là một loại giá trị

Nói cách khác, khi trình biên dịch biết nó thực sự chỉ là giá trị mặc định cho kiểu :)

(Thật thú vị, tôi chắc chắn 99,9% sẽ không gọi bất kỳ nhà new Foo()xây dựng tùy chỉnh nào mà bạn có thể đã tạo. Bạn không thể tạo một công cụ xây dựng như vậy trong một loại giá trị trong C #, nhưng bạn có thể làm như vậy trong IL.)

Bạn có thể sử dụng default(Foo)tùy chọn cho bất kỳ loại.


Bây giờ tại sao thông báo lỗi trình biên dịch không cho tôi biết điều này, trình biên dịch có thể kiểm tra trường hợp của Guid.Empty và đưa ra một thông báo hữu ích hơn.
Ian Ringrose

4
@Ian Ringrose: Tôi không nghĩ rằng trình biên dịch nên có các thông điệp cụ thể về loại nói chung, phải trung thực.
Jon Skeet

2
Đặt tham số thành mặc định cho một đối tượng mới sẽ tạo ra một đối tượng mới mỗi khi phương thức được gọi trong PHP; nhưng chỉ tạo một đối tượng cho toàn bộ chương trình trong Python. Thực sự, tôi coi đây là một trong số rất ít lỗi thiết kế của Python . Tôi hơi mừng vì C # (và VB.Net) đã tránh được vấn đề này bằng cách đơn giản không cho phép các đối tượng mới trong các tham số mặc định ... mặc dù có những lúc khả năng này thực sự tốt trong PHP.
BlueRaja - Daniel Pflughoeft

1
Điều gì có thể là lý do mà điều tương tự này sẽ không hoạt động trong hành động của bộ điều khiển ASP.NET MVC? Tất cả các tham số tùy chọn khác đều hoạt động (int, chuỗi) nhưng nó không hoạt động với GUID, nói "Lỗi máy chủ trong '/' Ứng dụng. Từ điển tham số chứa mục nhập null cho tham số 'categoryId' của loại không nullable 'System.Guid '.. "Mã biên dịch tốt với cả hai thông số kỹ thuật (mặc định (Hướng dẫn) và với Hướng dẫn mới ()) nhưng phát sinh lỗi này.
mê cung

@mare: Tôi không biết, tôi sợ. Một lựa chọn khác là sử dụng Nullable<Guid>, có khả năng.
Jon Skeet

18

Bạn không thể sử dụng:

default ( Guid ) ?


1
Số Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
đệ quy

4
Xin lỗi, ý tôi không phải sao ?? là một nhà điều hành nhưng là một dấu hỏi nhấn mạnh - tôi sẽ chỉnh sửa!
Nick

9

Câu trả lời được chấp nhận không hoạt động trong ASP.NET MVC và gây ra lỗi thời gian chạy này:

[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)' ....

Thay vào đó, bạn có thể làm như sau:

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

Tôi thấy lý do bỏ phiếu giảm: Câu hỏi chỉ định rằng API không thể thay đổi để sử dụng Nullable <Guid> - đủ công bằng
Majix

Trừ khi bạn muốn gán rõ ràng tham số "tùy chọn" với giá trị Guid trống, tôi nghĩ đây là cách tự nhiên nhất để xác định tham số tùy chọn của loại Guid.
Gonzalo Méndez

4

Trình biên dịch khá chính xác; Guid.Emptykhông phải là hằng số thời gian biên dịch. Bạn có thể thử thực hiện quá tải phương thức như thế này:

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

Tôi nói rằng tôi không muốn thay đổi API, phương pháp tôi đang cố gắng để chế ngự đã vượt qua 10 parms!
Ian Ringrose

@Ian Ringrose, mặc dù tôi đồng ý với Guid x = default(Guid)tư cách là giải pháp, hãy nhớ rằng việc thêm một chức năng quá tải khác không làm phức tạp API hơn bất kỳ việc thêm đối số tùy chọn. Đó thực sự là những gì một đối số tùy chọn làm gì.
mười giờ

@tenfour, nó sẽ phải có quá nhiều chức năng mới để làm tương tự như 10 parms tùy chọn!
Ian Ringrose

Nếu bạn có một hàm công khai yêu cầu hơn mười tham số, có thể tạo một đối số tùy chọn không thực sự là một sửa chữa ... Ngoài ra, nhìn lại câu hỏi ban đầu, bạn thực sự nói rằng bạn không muốn "thay đổi API "(và giống như tenfour chỉ ra, sự khác biệt giữa quá tải rõ ràng và đối số tùy chọn là tối thiểu trong thực tế) nhưng không có đề cập nào trong câu hỏi về danh sách tham số là loại quái vật đó.
một CVn

Trên thực tế, đây là một câu trả lời hoàn hảo cho vấn đề.
PKD
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.