Là loại nullable thích số ma thuật?


22

Gần đây tôi đã có một chút tranh luận với đồng nghiệp. Chúng tôi đặc biệt sử dụng C #, nhưng điều này có thể áp dụng cho bất kỳ ngôn ngữ nào có loại không thể. Nói ví dụ bạn có một giá trị đại diện cho tối đa. Tuy nhiên, giá trị tối đa này là tùy chọn. Tôi lập luận rằng một số không có giá trị sẽ thích hợp hơn. Đồng nghiệp của tôi ủng hộ việc sử dụng số không, trích dẫn tiền lệ. Cấp, những thứ như ổ cắm mạng thường được sử dụng bằng không để thể hiện thời gian chờ không giới hạn. Nếu tôi đã viết mã xử lý các ổ cắm ngày hôm nay, cá nhân tôi sẽ sử dụng một giá trị vô giá trị, vì tôi cảm thấy nó sẽ đại diện tốt hơn cho thực tế là KHÔNG có thời gian chờ.

Đại diện nào tốt hơn? Cả hai đều yêu cầu kiểm tra điều kiện cho giá trị có nghĩa là "không", nhưng tôi tin rằng một loại không thể truyền đạt ý định tốt hơn một chút.


6
Nếu một số được sử dụng, đặt nó trong một hằng số, không trực tiếp trong mã.
Renato Dinhani

@ RenatoDinhaniConceição không thể là một quy tắc chung. Nếu không, bạn kết thúc phần mềm mã hóa mọi thứ.
Simon Bergot

Câu trả lời:


24

Xem xét:

  • Ngôn ngữ

  • Khung,

  • Bối cảnh.

1. Ngôn ngữ

Sử dụng có thể là một giải pháp tối đa.

  • JavaScript, ví dụ, có một vô cùng. C # không¹.

  • Ada, ví dụ, có phạm vi. C # không.

Trong C #, có int.MaxValue, nhưng bạn không thể sử dụng nó trong trường hợp của bạn. int.MaxValuelà số nguyên tối đa, 2.147.483.647. Nếu trong mã của bạn, bạn có giá trị tối đa của một cái gì đó, như áp lực tối đa được chấp nhận trước khi một thứ gì đó phát nổ, sử dụng 2.147.483.647 không có ý nghĩa.

2. Khung

.NET Framework không nhất quán ở điểm này và việc sử dụng các giá trị ma thuật của nó có thể bị chỉ trích.

Ví dụ, "Hello".IndexOf("Z")trả về một giá trị ma thuật -1. Nó có thể làm cho nó dễ dàng hơn (phải không?) Để thao túng kết quả:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

thay vì sử dụng cấu trúc tùy chỉnh:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

nhưng không trực quan chút nào Tại sao -1và không -123? Một người mới bắt đầu cũng có thể nhầm tưởng rằng điều đó 0có nghĩa là "Không tìm thấy" quá hoặc chỉ gõ nhầm (position >= 0).

3. Bối cảnh

Nếu mã của bạn liên quan đến thời gian chờ trong ổ cắm mạng, sử dụng thứ gì đó được mọi người sử dụng trong nhiều thập kỷ vì mục đích nhất quán không phải là ý tưởng tồi . Đặc biệt, 0trong một khoảng thời gian chờ rất rõ ràng: đó là một giá trị không thể bằng không. Sử dụng một lớp tùy chỉnh trong trường hợp này có thể khiến mọi thứ trở nên khó hiểu hơn:

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • Tôi có thể đặt Durationthành 0 nếu IsTimeoutEnabledlà đúng không?
  • Nếu IsTimeoutEnabledsai, điều gì xảy ra nếu tôi đặt Durationthành 100?

Điều này có thể dẫn đến nhiều sai lầm. Hãy tưởng tượng đoạn mã sau:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

Các hoạt động trong mười giây. Bạn có thể thấy những gì sai với mã này, mà không cần đọc tài liệu của Timeoutlớp không?

Phần kết luận

  • nullthể hiện tốt ý tưởng rằng giá trị không có ở đây. Nó không được cung cấp. Không có sẵn. Đó không phải là một số, cũng không phải là một chuỗi 0 / trống hoặc bất cứ điều gì. Đừng sử dụng nó cho các giá trị tối đa hoặc tối thiểu.

  • int.MaxValuecó liên quan mạnh mẽ đến chính ngôn ngữ. Không sử dụng int.MaxValuecho giới hạn tốc độ tối đa của Vehiclelớp hoặc tốc độ tối đa chấp nhận được đối với máy bay, v.v.

  • Tránh các giá trị ma thuật như -1trong mã của bạn. Chúng gây hiểu lầm và dẫn đến sai lầm trong mã.

  • Tạo lớp của riêng bạn, điều này sẽ đơn giản hơn, với các giá trị tối thiểu / tối đa được chỉ định. Ví dụ VehicleSpeedcó thể có VehicleSpeed.MaxValue.

  • Không tuân theo bất kỳ hướng dẫn nào trước đây và sử dụng các giá trị ma thuật nếu đó là quy ước chung trong nhiều thập kỷ trong một lĩnh vực rất cụ thể, được sử dụng bởi hầu hết mọi người viết mã trong lĩnh vực này.

  • Đừng quên kết hợp các phương pháp. Ví dụ:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

Bạn có thể tạo loại của riêng bạn bao gồm vô cực. Ở đây, tôi chỉ nói về intloại bản địa .


1
"sử dụng thứ gì đó được mọi người sử dụng trong nhiều thập kỷ vì mục đích nhất quán không phải là ý tưởng tồi" / "Đừng làm theo bất kỳ hướng dẫn nào trước đây và sử dụng các giá trị ma thuật nếu đó là quy ước chung trong nhiều thập kỷ trong một lĩnh vực rất cụ thể, được sử dụng bởi hầu hết mọi người viết mã trong lĩnh vực này. " - Tôi nghĩ có một lỗi đánh máy ở đâu đó?
deworde

1
@deworde Tôi tin rằng MainMa đang đề cập đến các hướng dẫn mà chính ông đã đưa ra ở trên đó.
Joshua Drake

1
Tôi không đồng ý với ví dụ indexOf, vì -1 nằm ngoài chuỗi, mà Z chắc chắn là như vậy.
Joshua Drake

5
"JavaScript, ví dụ, có vô hạn. C # không." - hả
BlueRaja - Daniel Pflughoeft

+1 đặc biệt cho "Tạo lớp của riêng bạn", đó là những gì tôi đã đề xuất. Bất cứ khi nào trần intkhông thể hiện đủ về loại để hạn chế vấn đề, hãy xem xét một cấu trúc mới có nhiều thông tin hơn (ví dụ: các cấu trúc đại diện cho các giá trị ma thuật, ví dụ, hoặc một enum trên đó để chỉ ra). Hoặc xem xét lập trình hợp đồng hoặc một số giải pháp khác, nhưng tôi nghĩ một cấu trúc tùy chỉnh là đơn giản nhất.
CodexArcanum

12

Null không tốt hơn một con số ma thuật.

Điều quan trọng là TÊN các giá trị có hiệu ứng ma thuật, nếu bạn phải có các giá trị đó và để đảm bảo rằng các định nghĩa của các tên đó ở đâu đó sẽ được nhìn thấy bởi bất kỳ ai va vào giá trị ma thuật và wtf.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!

2
Ok, có thể nó phụ thuộc nhiều hơn vào ngôn ngữ, nhưng trong C #, bạn có thể sẽ làm: if (timeout.HasValue) thay vì so sánh trực tiếp với null.
Matt H

2
Null không tệ hơn số ma thuật. Với số ma thuật, bạn không bao giờ biết số ma thuật là gì ... nó có thể là 0, -1 hoặc thứ gì khác. null chỉ là null.
marco-fiset

9
Null có nghĩa là không có giá trị. Đây là khái niệm mà rất nhiều con số ma thuật đang có ý định thể hiện. Có thể sử dụng null với loại nullable là một giải pháp tốt hơn nhiều so với việc chọn một giá trị tùy ý từ phạm vi các giá trị có thể có cho một loại dữ liệu.
17 trên 26 tháng

2
Sử dụng "null" làm giá trị ma thuật của bạn, nếu loại của bạn có "null", thì tốt. Điều quan trọng là TÊN nó, bởi vì chắc chắn rằng anh chàng tiếp theo sẽ không biết ý của bạn là gì. Null có thể có nghĩa là "vô cùng", "chưa được chỉ định", "lỗi trong mã đã tạo cấu trúc dữ liệu" hoặc bất kỳ số lượng nào khác. Chỉ một cái tên cho phép lập trình viên tiếp theo biết rằng bạn có nghĩa là giá trị đó ở đó và hành vi mà bạn muốn nó kích hoạt.
mjfgates

1
@CodeInChaos: Tôi biết bạn có thể làm cả hai, nhưng tôi thích HasValue. Tôi thực sự không phải là một fan hâm mộ lớn của null nói chung, nhưng các loại nullable khi sử dụng HasValue cảm thấy gần gũi hơn với một loại Tùy chọn / Có thể với tôi, mà tôi là một người hâm mộ.
Matt H

10

MAGIC_NUMBERmã nên tuyệt đối luôn luôn nên tránh bất cứ nơi nào có thể. nulllà một biểu hiện rõ ràng hơn của ý định.


6

Trong C #, nhiều lớp CLR có Emptythành viên tĩnh :

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

Điều này giúp bạn không phải nhớ nên sử dụng giá trị ma thuật hay sử dụng null để xây dựng một đối tượng trống.

Nhưng nếu bạn đang xử lý một loại giá trị đơn giản như một int? Trong trường hợp đó, hãy xem xét liệu bạn có trở thành nạn nhân của Nỗi ám ảnh nguyên thủy hay không . Hoàn toàn có khả năng rằng thuộc tính số rõ ràng đơn giản của bạn sẽ được hưởng lợi từ lớp hoặc cấu trúc của chính nó, cho phép bạn chỉ định Emptythành viên và cũng thêm các hành vi khác cụ thể cho loại giá trị đó.


3

Trong trường hợp này, giá trị null là một cách tuyệt vời để chỉ ra rằng không có giá trị tối đa. Nói chung khi trường hợp đặc biệt có nghĩa là giá trị trong câu hỏi không được áp dụng, bạn chỉ không muốn tính năng mà nó cấu hình, null là một dấu hiệu tốt về điều này.

Một vấn đề với việc sử dụng null để thể hiện các trường hợp đặc biệt là chỉ có một giá trị null và có thể có nhiều trường hợp đặc biệt. Trong trường hợp này, tôi sẽ chuyển một phép liệt kê dưới dạng tham số bổ sung, có thể chỉ ra trường hợp đặc biệt hoặc sử dụng giá trị int thông thường. (Đây thực chất là những gì mà Nullable <> làm cho bạn, mặc dù nó sử dụng boolean thay vì enum và kết hợp các tham số thành một cấu trúc duy nhất.)


3

Trong trường hợp này, tôi nghĩ rằng một loại nullable có ý nghĩa hoàn hảo.

Null có nghĩa là không có giá trị. Đây là một khái niệm khác biệt so với một số có giá trị 0.

Nếu bạn muốn nói "Nếu tôi không cung cấp cho bạn một giá trị, hãy sử dụng mức tối đa" thì việc chuyển thành null là cách chính xác để thể hiện điều đó.


1

Null: giá trị lỗi phổ biến, không xác định, khoảng trống hoặc không có giá trị.

Zero: Một giá trị thực tế, nhưng không nhất thiết phải hợp lý hoặc trực quan (trong ngữ cảnh này). Cũng là một giá trị phổ biến trong khởi tạo.

Trong bối cảnh vấn đề của bạn, timeoutInMillisecondstài sản là tùy chọn và không có đề cập rằng chi phí chung của phương pháp này sẽ không đủ điều kiện để trở thành một tùy chọn.

Kết luận: Có các trường hợp ngoại lệ và các giải pháp khác nhau tùy theo ngôn ngữ và miền; trong trường hợp này, tôi sẽ chọn Null. Trường hợp (tôi tin) một số người mắc phải lỗi này là khi họ không tách dữ liệu khỏi giao diện tốt. Họ chỉ mong bất kỳ khách hàng nào đọc tài liệu (hoặc triển khai) để xác định cách sử dụng / xử lý các giá trị đặc biệt này - các trường hợp đặc biệt rò rỉ vào chương trình của khách hàng và có thể không rõ ràng. Bằng cách thêm một lớp trừu tượng tốt, việc sử dụng có thể rõ ràng hơn nhiều.


0

Null tệ hơn để sử dụng hơn MagicNumber. Null đại diện cho ý tưởng được thể hiện tốt hơn, nhưng nó không nhất quán trên các nền tảng trong cách hành xử, sử dụng MagicNumberluôn hoạt động giống nhau có lợi.

tùy thuộc vào môi trường / ngôn ngữ được sử dụng null có thể

  • chỉ đơn giản là 0
  • có thể không phải là một giá trị pháp lý
  • có thể tạo ra kết quả bất ngờ do logic ba chiều

MagicNumber luôn luôn cư xử như vậy.


0

Nếu bạn quên kiểm tra số ma thuật (sẽ xảy ra đúng), thì một số ma thuật sẽ tiếp tục trong một thời gian ngắn với dữ liệu vô nghĩa. Tốt hơn nhiều để có một null gây ra một ngoại lệ càng sớm càng tốt.


-1

Null không phải là sự thay thế duy nhất cho một con số ma thuật.

public static int NO_TIMEOUT = 0;  // javaish

Null là ác. Trong ví dụ trên, bạn có thể thoát khỏi nó vì mã rõ ràng sẽ có thể xử lý null. Nhưng nói chung, điều xảy ra khi bạn bắt đầu chuyển null xung quanh là sớm hay muộn bạn sẽ có một ngoại lệ con trỏ null. Nó có thể không xảy ra khi bạn lần đầu tiên viết mã, nhưng mã được duy trì lâu hơn nhiều so với chỉ bản phát hành đầu tiên. Nó thường được duy trì bởi những người không biết nhiều về hệ thống như các nhà phát triển ban đầu.

Scala (ví dụ) có một lựa chọn tốt trong lớp Tùy chọn. Lớp Tùy chọn có một trong hai giá trị, Một số - bao bọc giá trị bạn thực sự muốn và Không có - không có giá trị.

Điều đó cho thấy rõ ràng với bất kỳ nhà phát triển nào rằng có thể không có giá trị và bạn có mã tốt hơn cho điều đó. Vâng, dù sao nó cũng nên làm cho nó rõ ràng.

Và không phải tất cả các số ma thuật là một vấn đề. Tùy thuộc vào bối cảnh 0, 1, 1024, vv tất cả có thể rõ ràng. 347? Vâng, đó là một trong những bạn nên tránh. :-)


4
-1: Biện minh "null là xấu xa".
deworde

4
Xác định bí danh cho một số không thay đổi thực tế rằng đó vẫn là một số ma thuật.
17 trên 26 tháng

Chà, có lẽ bạn có một định nghĩa khác về số ma thuật so với tôi. Vui lòng xem en.wikipedia.org/wiki/ Kẻ
Jon Strayer

1
Tôi đồng ý với Jon Strayer ở đây. Null là một ví dụ về ADT bằng ngôn ngữ không thực sự hỗ trợ ADT. OP có lẽ có thể thoát khỏi nó ở đây nhưng nói chung tôi xem xét bất kỳ ngôn ngữ nào không có lỗi đã làm các lập trình viên hơi thất bại.
Jeremy Wall
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.