Khi nào tôi nên sử dụng toán tử chuyển đổi loại ẩn của C #?


14

Trong C #, chúng ta có thể quá tải toán tử chuyển đổi ẩn như thế này (ví dụ từ MSDN ):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Do đó, chúng ta có thể có một loại, một loại giá trị tùy chỉnh , tự chuyển đổi một cách kỳ diệu sang loại khác (không liên quan), khiến khán giả hoang mang (cho đến khi họ nhìn vào hậu trường và thấy toán tử chuyển đổi ngầm, nghĩa là).

Tôi không thích để bất cứ ai đọc mã của tôi trong sự hoang mang. Tôi không nghĩ nhiều người làm như vậy.

Câu hỏi là, các trường hợp sử dụng của toán tử chuyển đổi kiểu ẩn sẽ không khiến mã của tôi khó hiểu hơn nhiều?


1
Ồ Tôi thực sự không biết điều này tồn tại. Không nhất thiết là nó là một thứ tốt để sử dụng; Tôi biết mọi người đã thực sự khó chịu với loại chức năng này - ẩn trong C ++.
Katana314

@ Katana314: Đó không phải là điều khiến mọi người khó chịu, nhưng về việc ai đó thêm quá tải (có thể là toán tử, hàm chuyển đổi, hàm tạo, hàm tự do hoặc hàm thành viên) với hành vi đáng ngạc nhiên, tốt nhất là đáng ngạc nhiên.
Ded repeatator

Tôi khuyên bạn nên đọc về "quá tải toán tử" trong C ++, cụ thể là các toán tử "truyền". Tôi nghi ngờ nhiều lập luận tương tự cho / chống lại là như nhau, ngoại trừ cuộc tranh luận đã diễn ra ba lần miễn là C # tồn tại với nhiều hơn nữa để đọc.

Câu trả lời:


18

Tôi chỉ đề xuất các chuyển đổi ngầm giữa các loại đại diện cho cùng một giá trị theo các cách khác nhau. Ví dụ:

  • Loại màu sắc khác nhau thích RGB, HSL, HSVCMYK.
  • Các đơn vị khác nhau cho cùng một số lượng vật lý ( Meterso với Inch).
  • Hệ thống tọa độ khác nhau (cực vs Cartesian).

Tuy nhiên, có một số nguyên tắc mạnh cho biết khi nào không phù hợp để xác định chuyển đổi ngầm định:

  • Nếu việc chuyển đổi gây ra sự mất mát đáng kể về độ chính xác hoặc phạm vi, thì nó không nên ẩn (ví dụ: từ float64 sang float32 hoặc từ dài sang int).
  • Nếu chuyển đổi có thể đưa ra một InvalidCastngoại lệ ( ), thì nó không nên ẩn.
  • Nếu chuyển đổi gây ra phân bổ heap mỗi lần nó được thực hiện, thì nó không nên ẩn.
  • Nếu chuyển đổi không phải là một O(1)hoạt động, thì nó không nên ẩn.
  • Nếu loại nguồn hoặc loại mục tiêu có thể thay đổi, thì chuyển đổi không nên ẩn.
  • Nếu việc chuyển đổi phụ thuộc vào một số loại bối cảnh (cơ sở dữ liệu, cài đặt văn hóa, cấu hình, hệ thống tệp, v.v.) thì không nên ẩn (tôi cũng không khuyến khích người vận hành chuyển đổi rõ ràng trong trường hợp này).

Bây giờ hãy nói rằng toán tử chuyển đổi của bạn f: T1 -> T2không vi phạm bất kỳ quy tắc nào ở trên, sau đó hành vi sau đây cho thấy mạnh mẽ rằng chuyển đổi có thể được ẩn:

  • Nếu a == bsau đó f(a) == f(b).
  • Nếu a != bsau đó f(a) != f(b).
  • Nếu a.ToString() == b.ToString()sau đó f(a).ToString() == f(b).ToString().
  • V.v. cho các hoạt động khác được xác định trên cả hai T1T2 .

Tất cả các ví dụ của bạn có lẽ là mất mát. Cho dù chúng có đủ chính xác hay không, ...
Ded repeatator

Vâng tôi nhận ra rằng :-). Tôi không thể nghĩ ra một thuật ngữ tốt hơn cho "mất mát". Ý tôi là "mất mát" là các chuyển đổi trong đó phạm vi hoặc độ chính xác giảm đáng kể. Ví dụ: từ float64 đến float32 hoặc từ dài đến int.
Elian Ebbing

Tôi nghĩ a! = B => f (a)! = F (b), có lẽ không nên áp dụng. Có rất nhiều hàm có thể trả về cùng một giá trị cho các đầu vào khác nhau, ví dụ: floor () và ceil () ở phía toán học
cdkMoose

@cdkMoose Tất nhiên bạn đúng, và đó là lý do tại sao tôi thấy các thuộc tính này nhiều hơn là "điểm thưởng", chứ không phải quy tắc. Thuộc tính thứ hai đơn giản có nghĩa là chức năng chuyển đổi là tiêm. Đây thường là trường hợp khi bạn chuyển đổi sang loại có phạm vi lớn hơn, ví dụ từ int32 đến int64.
Elian Ebbing

@cdkMoose Mặt khác, thuộc tính đầu tiên chỉ nói rằng hai giá trị trong cùng một lớp tương đương của T1(ngụ ý bởi ==mối quan hệ trênT1 ) luôn ánh xạ tới hai giá trị trong cùng một lớp tương đương T2. Bây giờ tôi nghĩ về nó, tôi đoán tài sản đầu tiên thực sự cần được yêu cầu cho một chuyển đổi ngầm.
Elian Ebbing

6

Câu hỏi là, các trường hợp sử dụng của toán tử chuyển đổi kiểu ẩn sẽ không khiến mã của tôi khó hiểu hơn nhiều?

Khi các loại không liên quan (với lập trình viên). Có những trường hợp (hiếm) trong đó bạn có hai loại không liên quan (liên quan đến mã), có liên quan thực sự (liên quan đến tên miền hoặc lập trình viên hợp lý).

Ví dụ, một số mã để thực hiện khớp chuỗi. Một kịch bản phổ biến là khớp một chuỗi bằng chữ. Thay vì gọi IsMatch(input, new Literal("some string")), một chuyển đổi ngầm cho phép bạn thoát khỏi buổi lễ đó - tiếng ồn trong mã - và tập trung vào chuỗi ký tự.

Hầu hết mọi lập trình viên sẽ nhìn thấy IsMatch(input, "some string")và nhanh chóng hiểu được những gì đang diễn ra. Nó làm cho mã của bạn rõ ràng hơn tại trang web cuộc gọi. Nói tóm lại, nó làm cho nó dễ dàng hơn một chút để hiểu những gì đang xảy ra, với một chi phí nhỏ về cách điều đó đang diễn ra.

Bây giờ, bạn có thể lập luận rằng một chức năng quá tải đơn giản để làm điều tương tự sẽ tốt hơn. Và nó là. Nhưng nếu loại điều này có mặt ở khắp mọi nơi, thì việc có một chuyển đổi sẽ sạch hơn (ít mã hơn, tăng tính nhất quán) hơn là thực hiện một đống quá tải hàm.

Và bạn có thể lập luận rằng tốt hơn là yêu cầu các lập trình viên tạo ra loại trung gian một cách rõ ràng để họ thấy "những gì đang thực sự xảy ra". Điều đó ít đơn giản hơn. Cá nhân, tôi nghĩ rằng ví dụ khớp chuỗi theo nghĩa đen rất rõ ràng về "những gì đang thực sự xảy ra" - lập trình viên không cần biết cơ chế về cách mọi thứ xảy ra. Bạn có biết làm thế nào tất cả mã của bạn được thực thi bởi các bộ xử lý khác nhau mà mã của bạn chạy trên không? Luôn luôn có một dòng trừu tượng nơi các lập trình viên ngừng quan tâm về cách thức hoạt động của một cái gì đó. Nếu bạn nghĩ rằng các bước chuyển đổi ngầm là quan trọng, thì đừng sử dụng chuyển đổi ngầm định. Nếu bạn nghĩ rằng họ chỉ làm lễ để giữ cho máy tính vui vẻ, và lập trình viên sẽ tốt hơn nếu không thấy tiếng ồn đó ở mọi nơi,


Điểm cuối cùng của bạn có thể và nên được thực hiện hơn nữa: Ngoài ra còn có một dòng mà một lập trình viên rất tốt, không quan tâm làm thế nào một cái gì đó được thực hiện, bởi vì đó không phải là hợp đồng.
Ded repeatator
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.