Hạn chế tuyên bố chuyển đổi C # - tại sao?


140

Khi viết một câu lệnh chuyển đổi, dường như có hai hạn chế về những gì bạn có thể bật trong các câu lệnh tình huống.

Ví dụ (và vâng, tôi biết, nếu bạn đang làm điều này có lẽ điều đó có nghĩa là kiến trúc hướng đối tượng (OO) của bạn là iffy - đây chỉ là một ví dụ giả định!),

  Type t = typeof(int);

  switch (t) {

    case typeof(int):
      Console.WriteLine("int!");
      break;

    case typeof(string):
      Console.WriteLine("string!");
      break;

    default:
      Console.WriteLine("unknown!");
      break;
  }

Ở đây, câu lệnh switch () không thành công với 'Giá trị của loại tích phân được mong đợi' và các câu lệnh trường hợp không thành công với 'Giá trị không đổi được mong đợi'.

Tại sao những hạn chế này được đưa ra, và biện minh cơ bản là gì? Tôi không thấy bất cứ lý do tại sao câu lệnh switch để chống chọi lại với chỉ phân tích tĩnh, và lý do tại sao giá trị hạnh phúc bật phải là không thể thiếu (có nghĩa là, nguyên thủy). Sự biện minh là gì?


1
Xem cách giải quyết có thể Có cách nào khác tốt hơn là 'bật loại' không?
Michael Freidgeim

Một tùy chọn khác để chuyển đổi các loại tích hợp là sử dụng Enum TypeCode .
Erik Philips

Câu trả lời:


98

Đây là bài viết gốc của tôi, đã gây ra một số tranh luận ... bởi vì nó sai :

Câu lệnh switch không giống với câu lệnh if-other. Mỗi trường hợp phải là duy nhất và được đánh giá tĩnh. Câu lệnh switch thực hiện một nhánh thời gian không đổi bất kể bạn có bao nhiêu trường hợp. Câu lệnh if-other đánh giá từng điều kiện cho đến khi nó tìm thấy một điều kiện đúng.


Trong thực tế, câu lệnh chuyển đổi C # không phải luôn luôn là một nhánh thời gian không đổi.

Trong một số trường hợp, trình biên dịch sẽ sử dụng câu lệnh chuyển đổi CIL, đây thực sự là một nhánh thời gian không đổi sử dụng bảng nhảy. Tuy nhiên, trong các trường hợp thưa thớt như được chỉ ra bởi Ivan Hamilton , trình biên dịch có thể tạo ra một thứ hoàn toàn khác.

Điều này thực sự khá dễ dàng để xác minh bằng cách viết các câu lệnh chuyển đổi C # khác nhau, một số thưa thớt, dày đặc và nhìn vào CIL kết quả với công cụ ildasm.exe.


4
Như đã lưu ý trong các câu trả lời khác (bao gồm cả của tôi), các yêu cầu đưa ra trong câu trả lời này là không chính xác. Tôi sẽ khuyên bạn nên xóa (nếu chỉ để tránh thực thi quan niệm sai lầm (có thể phổ biến) này).
mweerden

Xin vui lòng xem bài viết của tôi dưới đây, nơi tôi thể hiện, theo ý kiến ​​của tôi, rằng câu lệnh chuyển đổi thực hiện một nhánh thời gian không đổi.
Brian Consink

Cảm ơn bạn rất nhiều vì đã trả lời của bạn, Brian. Vui lòng xem câu trả lời của Ivan Hamilton ((48259) [ beta.stackoverflow.com/questions/44905/#48259] ). Tóm lại: bạn đang nói về switch hướng dẫn (của CIL) không giống với switchtuyên bố của C #.
mweerden

Tôi không tin trình biên dịch tạo phân nhánh thời gian liên tục khi bật chuỗi.
vẽ Noakes

Điều này có còn được áp dụng với khớp mẫu trong các câu lệnh chuyển đổi trong C # 7.0 không?
B. Darren Olson

114

Điều quan trọng là không nhầm lẫn giữa câu lệnh chuyển đổi C # với hướng dẫn chuyển đổi CIL.

Công tắc CIL là một bảng nhảy, yêu cầu một chỉ mục thành một tập hợp các địa chỉ nhảy.

Điều này chỉ hữu ích nếu các trường hợp của công tắc C # liền kề:

case 3: blah; break;
case 4: blah; break;
case 5: blah; break;

Nhưng ít sử dụng nếu chúng không:

case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;

(Bạn sẽ cần một bảng ~ 3000 mục có kích thước, chỉ có 3 vị trí được sử dụng)

Với các biểu thức không liền kề, trình biên dịch có thể bắt đầu thực hiện kiểm tra tuyến tính if-other-if-if.

Với các tập biểu thức không liền kề lớn hơn, trình biên dịch có thể bắt đầu bằng tìm kiếm cây nhị phân và cuối cùng là if-other-if-other vài mục cuối cùng.

Với các bộ biểu thức có chứa các cụm của các mục liền kề, trình biên dịch có thể tìm kiếm cây nhị phân và cuối cùng là một công tắc CIL.

Cái này chứa đầy "mays" & "mights", và nó phụ thuộc vào trình biên dịch (có thể khác với Mono hoặc Rotor).

Tôi đã sao chép kết quả của bạn trên máy của mình bằng các trường hợp liền kề:

tổng thời gian để thực hiện chuyển đổi 10 chiều, 10000 lần lặp (ms): 25.1383
thời gian gần đúng cho mỗi chuyển đổi 10 chiều (ms): 0,00251383

tổng thời gian để thực hiện chuyển đổi 50 cách, 10000 lần lặp (ms): 26.593
thời gian gần đúng cho mỗi chuyển đổi 50 cách (ms): 0,0026593

tổng thời gian để thực hiện chuyển đổi 5000 cách, 10000 lần lặp (ms): 23,7094
thời gian gần đúng cho mỗi chuyển đổi 5000 cách (ms): 0,00237094

tổng thời gian để thực hiện chuyển đổi 50000 cách, 10000 lần lặp (ms): 20,0933
thời gian gần đúng cho mỗi chuyển đổi 50000 cách (ms): 0,00200933

Sau đó, tôi cũng đã sử dụng các biểu thức trường hợp không liền kề:

tổng thời gian để thực hiện chuyển đổi 10 chiều, 10000 lần lặp (ms): 19.6189
thời gian gần đúng cho mỗi chuyển đổi 10 chiều (ms): 0,00196189

tổng thời gian để thực hiện chuyển đổi 500 cách, 10000 lần lặp (ms): 19.1664
thời gian gần đúng cho mỗi chuyển đổi 500 cách (ms): 0,00191664

tổng thời gian để thực hiện chuyển đổi 5000 cách, 10000 lần lặp (ms): 19.5871
thời gian gần đúng cho mỗi chuyển đổi 5000 cách (ms): 0,00195871

Một tuyên bố chuyển đổi trường hợp 50.000 không liền kề sẽ không biên dịch.
"Một biểu thức quá dài hoặc phức tạp để biên dịch gần 'ConsoleApplication1.Program.Main (chuỗi [])'

Điều thú vị ở đây là tìm kiếm cây nhị phân xuất hiện nhanh hơn một chút (có thể không theo thống kê) so với hướng dẫn chuyển đổi CIL.

Brian, bạn đã sử dụng từ " hằng ", có ý nghĩa rất rõ ràng từ góc độ lý thuyết phức tạp tính toán. Trong khi ví dụ số nguyên liền kề đơn giản có thể tạo ra CIL được coi là O (1) (không đổi), một ví dụ thưa thớt là O (log n) (logarit), các ví dụ cụm nằm ở đâu đó ở giữa và các ví dụ nhỏ là O (n) (tuyến tính ).

Điều này thậm chí không giải quyết được tình huống Chuỗi, trong đó một tĩnh Generic.Dictionary<string,int32>có thể được tạo và sẽ chịu chi phí nhất định khi sử dụng lần đầu. Hiệu suất ở đây sẽ phụ thuộc vào hiệu suất của Generic.Dictionary.

Nếu bạn kiểm tra Đặc tả ngôn ngữ C # (không phải thông số CIL), bạn sẽ thấy "15.7.2 Câu lệnh chuyển đổi" không đề cập đến "thời gian không đổi" hoặc việc triển khai cơ bản thậm chí sử dụng lệnh chuyển đổi CIL (rất cẩn thận khi giả sử những điều như vậy).

Vào cuối ngày, một công tắc C # chống lại một biểu thức số nguyên trên một hệ thống hiện đại là một hoạt động dưới micro giây và thường không đáng lo ngại.


Tất nhiên những thời gian này sẽ phụ thuộc vào máy móc và điều kiện. Tôi sẽ không chú ý đến các thử nghiệm thời gian này, thời lượng micro giây mà chúng ta đang nói đến bị lấn át bởi bất kỳ mã Real real nào đang được chạy (và bạn phải bao gồm một số mã thực tế, nếu không trình biên dịch sẽ tối ưu hóa chi nhánh), hoặc jitter trong hệ thống. Câu trả lời của tôi dựa trên việc sử dụng IL DASM để kiểm tra CIL được tạo bởi trình biên dịch C #. Tất nhiên, điều này không phải là cuối cùng, vì các hướng dẫn thực tế mà CPU chạy sau đó được tạo bởi JIT.

Tôi đã kiểm tra các hướng dẫn CPU cuối cùng thực sự được thực hiện trên máy x86 của mình và có thể xác nhận một công tắc thiết lập liền kề đơn giản thực hiện một số thứ như:

  jmp     ds:300025F0[eax*4]

Trường hợp tìm kiếm cây nhị phân có đầy đủ:

  cmp     ebx, 79Eh
  jg      3000352B
  cmp     ebx, 654h
  jg      300032BB
  
  cmp     ebx, 0F82h
  jz      30005EEE

Kết quả thí nghiệm của bạn làm tôi ngạc nhiên một chút. Bạn đã trao đổi của bạn với Brian's? Kết quả của anh ta cho thấy sự gia tăng với kích thước trong khi của bạn thì không. Tôi là một cái gì đó thiếu? Trong mọi trường hợp, cảm ơn đã trả lời rõ ràng.
mweerden

Thật khó để xác định chính xác thời gian với một thao tác nhỏ như vậy. Chúng tôi đã không chia sẻ mã, hoặc các thủ tục kiểm tra. Tôi không thể thấy tại sao thời gian của anh ta nên tăng cho các trường hợp liền kề. Của tôi nhanh hơn gấp 10 lần, vì vậy môi trường & mã kiểm tra có thể thay đổi rất nhiều.
Ivan Hamilton

23

Lý do đầu tiên xuất hiện trong tâm trí là lịch sử :

Vì hầu hết các lập trình viên C, C ++ và Java không quen với việc có các quyền tự do như vậy, nên họ không yêu cầu chúng.

Một lý do khác, hợp lệ hơn, là độ phức tạp của ngôn ngữ sẽ tăng lên :

Trước hết, các đối tượng nên được so sánh với .Equals()hoặc với ==toán tử? Cả hai đều có giá trị trong một số trường hợp. Chúng ta có nên giới thiệu cú pháp mới để làm điều này? Chúng ta có nên cho phép lập trình viên giới thiệu phương pháp so sánh của riêng họ?

Ngoài ra, cho phép bật các đối tượng sẽ phá vỡ các giả định cơ bản về câu lệnh chuyển đổi . Có hai quy tắc điều chỉnh câu lệnh chuyển đổi mà trình biên dịch sẽ không thể thực thi nếu các đối tượng được phép bật (xem đặc tả ngôn ngữ C # phiên bản 3.0 , §8.7.2):

  • Các giá trị của nhãn chuyển đổi là không đổi
  • Các giá trị của nhãn chuyển đổi là khác biệt (do đó chỉ có thể chọn một khối chuyển đổi cho biểu thức chuyển đổi đã cho)

Xem xét ví dụ mã này trong trường hợp giả thuyết rằng các giá trị trường hợp không cố định được cho phép:

void DoIt()
{
    String foo = "bar";
    Switch(foo, foo);
}

void Switch(String val1, String val2)
{
    switch ("bar")
    {
        // The compiler will not know that val1 and val2 are not distinct
        case val1:
            // Is this case block selected?
            break;
        case val2:
            // Or this one?
            break;
        case "bar":
            // Or perhaps this one?
            break;
    }
}

Mã sẽ làm gì? Điều gì xảy ra nếu các báo cáo trường hợp được sắp xếp lại? Thật vậy, một trong những lý do khiến C # thực hiện chuyển đổi bất hợp pháp là các câu lệnh chuyển đổi có thể được sắp xếp lại một cách tùy tiện.

Các quy tắc này được đặt ra vì một lý do - để lập trình viên có thể, bằng cách nhìn vào một khối trường hợp, biết chắc chắn điều kiện chính xác mà theo đó khối được nhập. Khi câu lệnh chuyển đổi nói trên phát triển thành 100 dòng trở lên (và nó sẽ), kiến ​​thức như vậy là vô giá.


2
Lưu ý về việc sắp xếp lại công tắc. Rơi qua là hợp pháp nếu trường hợp không có mã. Chẳng hạn như, Trường hợp 1: Trường hợp 2: Console.WriteLine ("Hi"); phá vỡ;
Joel McBeth

10

Nhân tiện, VB, có cùng kiến ​​trúc cơ bản, cho phép các Select Casecâu lệnh linh hoạt hơn nhiều (đoạn mã trên sẽ hoạt động trong VB) và vẫn tạo ra mã hiệu quả trong trường hợp có thể, do đó, đối số của ràng buộc công nghệ phải được xem xét cẩn thận.


1
Các Select Caseen VB là rất linh hoạt và tiết kiệm thời gian siêu. Tôi nhớ nó rất nhiều.
Eduardo Molteni

@EduardoMolteni Chuyển sang F # rồi. Nó làm cho các thiết bị chuyển mạch của Pascal và VB có vẻ như là những đứa trẻ ngốc.
Luaan

10

Hầu hết, những hạn chế đó được đặt ra vì các nhà thiết kế ngôn ngữ. Sự biện minh cơ bản có thể là khả năng tương thích với lịch sử languange, lý tưởng hoặc đơn giản hóa thiết kế trình biên dịch.

Trình biên dịch có thể (và không) chọn:

  • tạo một tuyên bố if-other lớn
  • sử dụng hướng dẫn chuyển đổi MSIL (bảng nhảy)
  • xây dựng một Generic.Dixi <chuỗi, int32>, đưa vào sử dụng lần đầu tiên và gọi Generic.Dixi <> :: TryGetValue () để một chỉ mục chuyển đến một lệnh chuyển đổi MSIL (bảng nhảy)
  • sử dụng kết hợp các bước nhảy "switch" if-elses & MSIL

Câu lệnh chuyển đổi KHÔNG phải là một nhánh thời gian không đổi. Trình biên dịch có thể tìm thấy các phím tắt (sử dụng các hàm băm, v.v.), nhưng các trường hợp phức tạp hơn sẽ tạo ra mã MSIL phức tạp hơn với một số trường hợp phân nhánh sớm hơn các trường hợp khác.

Để xử lý trường hợp String, trình biên dịch sẽ kết thúc (tại một số điểm) bằng cách sử dụng a.Equals (b) (và có thể là a.GetHashCode ()). Tôi nghĩ rằng trình biên dịch sẽ sử dụng bất kỳ đối tượng nào thỏa mãn các ràng buộc này.

Đối với nhu cầu về biểu thức trường hợp tĩnh ... một số tối ưu hóa đó (băm, bộ đệm, v.v.) sẽ không khả dụng nếu các biểu thức trường hợp không xác định. Nhưng chúng ta đã thấy rằng đôi khi trình biên dịch chỉ chọn đường if-if-if-if đơn giản khác ...

Chỉnh sửa: lomaxx - Hiểu biết của bạn về toán tử "typeof" là không chính xác. Toán tử "typeof" được sử dụng để lấy đối tượng System.Type cho một kiểu (không liên quan gì đến siêu kiểu hoặc giao diện của nó). Kiểm tra tính tương thích thời gian chạy của một đối tượng với một loại nhất định là công việc của toán tử "là". Việc sử dụng "typeof" ở đây để thể hiện một đối tượng là không liên quan.


6

Trong khi về chủ đề, theo Jeff Atwood, tuyên bố chuyển đổi là một hành động tàn bạo lập trình . Sử dụng chúng một cách tiết kiệm.

Bạn thường có thể hoàn thành nhiệm vụ tương tự bằng cách sử dụng bảng. Ví dụ:

var table = new Dictionary<Type, string>()
{
   { typeof(int), "it's an int!" }
   { typeof(string), "it's a string!" }
};

Type someType = typeof(int);
Console.WriteLine(table[someType]);

7
Bạn đang nghiêm túc trích dẫn bài viết của ai đó trên Twitter mà không có bằng chứng? Ít nhất là liên kết đến một nguồn đáng tin cậy.
Ivan Hamilton

4
Đó là từ một nguồn đáng tin cậy; bài đăng trên Twitter được đề cập là của Jeff Atwood, tác giả của trang web bạn đang xem. :-) Jeff có một số bài đăng trên blog về chủ đề này nếu bạn tò mò.
Judah Gabriel Himango

Tôi tin rằng đó là toàn bộ BS - dù Jeff Atwood có viết hay không. Thật buồn cười khi câu lệnh chuyển đổi cho vay để xử lý các máy trạng thái và các ví dụ khác về việc thay đổi dòng mã dựa trên giá trị của một enumloại. Không phải ngẫu nhiên mà intellisense tự động đưa ra câu lệnh chuyển đổi khi bạn bật một biến của một enumloại.
Jonathon Reinhart

@JonathonReinhart Vâng, tôi nghĩ đó là vấn đề - có nhiều cách tốt hơn để xử lý mã đa hình hơn là sử dụng switchcâu lệnh. Anh ấy không nói rằng bạn không nên viết máy trạng thái, chỉ là bạn có thể làm điều tương tự bằng cách sử dụng các loại cụ thể đẹp. Tất nhiên, điều này dễ dàng hơn nhiều trong các ngôn ngữ như F # có các loại có thể dễ dàng bao gồm các trạng thái khá phức tạp. Ví dụ của bạn, bạn có thể sử dụng các hiệp hội bị phân biệt đối xử trong đó trạng thái trở thành một phần của loại và thay thế switchbằng khớp mẫu. Hoặc sử dụng giao diện, ví dụ.
Luaan

Câu trả lời / câu hỏi cũ, nhưng tôi có thể nghĩ rằng (sửa tôi nếu tôi sai) Dictionarysẽ chậm hơn đáng kể so với một switchtuyên bố được tối ưu hóa ...?
Paul

6

Tôi không thấy bất kỳ lý do nào khiến câu lệnh chuyển đổi phải chuyển sang phân tích tĩnh

Đúng vậy, nó không đến, và nhiều ngôn ngữ nào trên thực tế sử dụng báo cáo chuyển đổi năng động. Tuy nhiên, điều này có nghĩa là việc sắp xếp lại các mệnh đề "trường hợp" có thể thay đổi hành vi của mã.

Có một số thông tin thú vị đằng sau các quyết định thiết kế đã đi vào "chuyển đổi" ở đây: Tại sao câu lệnh chuyển đổi C # được thiết kế để không cho phép rơi, nhưng vẫn cần phải nghỉ?

Cho phép các biểu thức trường hợp động có thể dẫn đến các quái vật như mã PHP này:

switch (true) {
    case a == 5:
        ...
        break;
    case b == 10:
        ...
        break;
}

mà thẳng thắn chỉ nên sử dụng if-elsetuyên bố.


1
Đó là những gì tôi yêu thích về PHP (bây giờ khi tôi đang chuyển sang C #), đó là sự tự do. Cùng với đó là sự tự do để viết mã xấu, nhưng đây là điều tôi thực sự nhớ trong C #
Silkfire

5

Microsoft cuối cùng đã nghe thấy bạn!

Bây giờ với C # 7, bạn có thể:

switch(shape)
{
case Circle c:
    WriteLine($"circle with radius {c.Radius}");
    break;
case Rectangle s when (s.Length == s.Height):
    WriteLine($"{s.Length} x {s.Height} square");
    break;
case Rectangle r:
    WriteLine($"{r.Length} x {r.Height} rectangle");
    break;
default:
    WriteLine("<unknown shape>");
    break;
case null:
    throw new ArgumentNullException(nameof(shape));
}

3

Đây không phải là lý do tại sao, nhưng phần đặc tả C # 8.7.2 nêu rõ như sau:

Kiểu quản lý của một câu lệnh chuyển đổi được thiết lập bởi biểu thức chuyển đổi. Nếu loại biểu thức chuyển đổi là sbyte, byte, short, ushort, int, uint, long, ulong, char, chuỗi hoặc enum-type, thì đó là loại quản lý của câu lệnh chuyển đổi. Mặt khác, chính xác một chuyển đổi ngầm định do người dùng định nghĩa (§6.4) phải tồn tại từ loại biểu thức chuyển đổi sang một trong các loại quản trị có thể sau: sbyte, byte, short, ushort, int, uint, long, ulong, char, chuỗi . Nếu không có chuyển đổi ngầm định nào tồn tại hoặc nếu có nhiều hơn một chuyển đổi ngầm định đó xảy ra, sẽ xảy ra lỗi thời gian biên dịch.

Thông số kỹ thuật của C # 3.0 được đặt tại: http://doad.microsoft.com/doad/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20L Language% 20Specifying.doc


3

Câu trả lời của Giu-đa ở trên đã cho tôi một ý tưởng. Bạn có thể "giả mạo" hành vi chuyển đổi của OP ở trên bằng cách sử dụng Dictionary<Type, Func<T>:

Dictionary<Type, Func<object, string,  string>> typeTable = new Dictionary<Type, Func<object, string, string>>();
typeTable.Add(typeof(int), (o, s) =>
                    {
                        return string.Format("{0}: {1}", s, o.ToString());
                    });

Điều này cho phép bạn liên kết hành vi với một loại theo cùng kiểu với câu lệnh switch. Tôi tin rằng nó có thêm lợi ích của việc được khóa thay vì bảng nhảy kiểu chuyển đổi khi được biên dịch sang IL.


0

Tôi cho rằng không có lý do cơ bản nào khiến trình biên dịch không thể tự động dịch câu lệnh chuyển đổi của bạn thành:

if (t == typeof(int))
{
...
}
elseif (t == typeof(string))
{
...
}
...

Nhưng không có nhiều bằng cách đó.

Một tuyên bố trường hợp về các loại tích phân cho phép trình biên dịch thực hiện một số tối ưu hóa:

  1. Không có sự trùng lặp (trừ khi bạn sao chép nhãn trường hợp mà trình biên dịch phát hiện). Trong ví dụ của bạn t có thể phù hợp với nhiều loại do thừa kế. Trận đấu đầu tiên có nên được thực hiện? Tất cả bọn họ?

  2. Trình biên dịch có thể chọn thực hiện một câu lệnh chuyển đổi qua một loại tích phân bằng một bảng nhảy để tránh tất cả các so sánh. Nếu bạn đang chuyển sang một bảng liệt kê có các giá trị nguyên từ 0 đến 100 thì nó sẽ tạo ra một mảng có 100 con trỏ trong đó, một cho mỗi câu lệnh chuyển đổi. Trong thời gian chạy, nó chỉ đơn giản là tìm địa chỉ từ mảng dựa trên giá trị nguyên được bật. Điều này làm cho hiệu suất thời gian chạy tốt hơn nhiều so với thực hiện 100 so sánh.


1
Một sự phức tạp quan trọng cần lưu ý ở đây là mô hình bộ nhớ .NET có một số đảm bảo chắc chắn làm cho mã giả của bạn không tương đương chính xác với (giả thuyết, C # không hợp lệ ) switch (t) { case typeof(int): ... }bởi vì bản dịch của bạn ngụ ý rằng biến t đó phải được lấy từ bộ nhớ hai lần nếu t != typeof(int), trong khi cái sau sẽ (chính thức) luôn đọc giá trị của t chính xác một lần . Sự khác biệt này có thể phá vỡ tính chính xác của mã đồng thời dựa trên những đảm bảo tuyệt vời đó. Để biết thêm thông tin về điều này, hãy xem Lập trình đồng thời
Glenn Slayden

0

Theo tài liệu tuyên bố chuyển đổi nếu có một cách rõ ràng để ngầm chuyển đổi đối tượng thành một loại tích phân, thì nó sẽ được cho phép. Tôi nghĩ rằng bạn đang mong đợi một hành vi trong đó cho mỗi câu lệnh trường hợp sẽ được thay thế bằng nó if (t == typeof(int)), nhưng điều đó sẽ mở ra cả một hộp giun khi bạn làm quá tải toán tử đó. Hành vi sẽ thay đổi khi chi tiết triển khai cho câu lệnh chuyển đổi thay đổi nếu bạn viết == ghi đè không chính xác. Bằng cách giảm các so sánh với các loại và chuỗi tích phân và những thứ có thể được giảm xuống thành các loại tích phân (và được dự định), chúng sẽ tránh được các vấn đề tiềm ẩn.


0

đã viết:

"Câu lệnh switch thực hiện một nhánh thời gian không đổi bất kể bạn có bao nhiêu trường hợp."

Vì ngôn ngữ cho phép loại chuỗi được sử dụng trong câu lệnh chuyển đổi, tôi cho rằng trình biên dịch không thể tạo mã để thực hiện nhánh thời gian không đổi cho loại này và cần tạo kiểu if-then.

@mweerden - À tôi hiểu rồi. Cảm ơn.

Tôi không có nhiều kinh nghiệm về C # và .NET nhưng có vẻ như các nhà thiết kế ngôn ngữ không cho phép truy cập tĩnh vào hệ thống loại trừ trong các trường hợp hẹp. Các typeof từ khóa trả về một đối tượng vì vậy đây có thể truy cập tại chỉ chạy theo thời gian.


0

Tôi nghĩ Henk đóng đinh nó với điều "không có quyền truy cập sttatic vào hệ thống loại"

Một tùy chọn khác là không có thứ tự cho các loại trong đó có thể là số và chuỗi. Do đó, một loại chuyển đổi sẽ không thể xây dựng một cây tìm kiếm nhị phân, chỉ là một tìm kiếm tuyến tính.


0

Tôi đồng ý với nhận xét này rằng sử dụng phương pháp điều khiển bảng thường tốt hơn.

Trong C # 1.0, điều này là không thể bởi vì nó không có chung chung và đại biểu ẩn danh. Các phiên bản mới của C # có giàn giáo để thực hiện công việc này. Có một ký hiệu cho các đối tượng bằng chữ cũng là giúp.


0

Tôi hầu như không có kiến ​​thức về C #, nhưng tôi nghi ngờ rằng một trong hai chuyển đổi chỉ được thực hiện khi nó xảy ra trong các ngôn ngữ khác mà không nghĩ đến việc làm cho nó chung chung hơn hoặc nhà phát triển đã quyết định rằng việc mở rộng nó không đáng.

Nói đúng ra bạn hoàn toàn đúng rằng không có lý do gì để đặt những hạn chế này lên nó. Người ta có thể nghi ngờ rằng nguyên nhân là đối với các trường hợp được phép thực hiện rất hiệu quả (theo đề nghị của Brian Ensink ( 44.921 )), nhưng tôi nghi ngờ việc thực hiện rất hiệu quả (wrt if-báo cáo) nếu tôi sử dụng số nguyên và một số trường hợp ngẫu nhiên (ví dụ 345, -4574 và 1234203). Và trong mọi trường hợp, tác hại của việc cho phép nó đối với mọi thứ (hoặc ít nhất là nhiều hơn) và nói rằng nó chỉ hiệu quả đối với các trường hợp cụ thể (chẳng hạn như (gần như) các số liên tiếp).

Tuy nhiên, tôi có thể tưởng tượng rằng người ta có thể muốn loại trừ các loại vì các lý do như loại được đưa ra bởi lomaxx ( 44918 ).

Chỉnh sửa: @Henk ( 44970 ): Nếu Chuỗi được chia sẻ tối đa, các chuỗi có nội dung bằng nhau cũng sẽ là con trỏ đến cùng một vị trí bộ nhớ. Sau đó, nếu bạn có thể chắc chắn rằng các chuỗi được sử dụng trong các trường hợp được lưu trữ liên tiếp trong bộ nhớ, bạn có thể thực hiện rất hiệu quả công tắc (nghĩa là thực hiện theo thứ tự 2 so sánh, thêm và hai lần nhảy).


0

C # 8 cho phép bạn giải quyết vấn đề này một cách thanh lịch và gọn gàng bằng cách sử dụng biểu thức chuyển đổi:

public string GetTypeName(object obj)
{
    return obj switch
    {
        int i => "Int32",
        string s => "String",
        { } => "Unknown",
        _ => throw new ArgumentNullException(nameof(obj))
    };
}

Kết quả là bạn nhận được:

Console.WriteLine(GetTypeName(obj: 1));           // Int32
Console.WriteLine(GetTypeName(obj: "string"));    // String
Console.WriteLine(GetTypeName(obj: 1.2));         // Unknown
Console.WriteLine(GetTypeName(obj: null));        // System.ArgumentNullException

Bạn có thể đọc thêm về tính năng mới ở đây .

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.