Tại sao nên tránh casting? [đóng cửa]


97

Nói chung, tôi tránh các kiểu truyền càng nhiều càng tốt vì tôi có ấn tượng rằng đó là hoạt động viết mã kém và có thể bị phạt khi thực hiện.

Nhưng nếu ai đó yêu cầu tôi giải thích chính xác lý do tại sao lại như vậy, có lẽ tôi sẽ nhìn họ như một con nai trong đèn pha.

Vậy tại sao / khi nào thì quá trình truyền xấu?

Nó là chung cho java, c #, c ++ hay mọi môi trường thời gian chạy khác nhau đều xử lý nó theo các điều khoản riêng?

Thông tin cụ thể cho một ngôn ngữ bất kỳ đều được hoan nghênh, ví dụ tại sao nó tệ trong c ++?


3
Bạn có ấn tượng này ở đâu?
Oded

8
Tôi có ấn tượng này vì tôi chưa bao giờ đọc sách hoặc gặp một lập trình viên nói "CASTING SOOOO GOOOOD !!!"
LoudNPossablyWrong

15
Câu trả lời cho C ++ chắc chắn khác với câu trả lời cho C #. Điều này rất cụ thể về ngôn ngữ. Các câu trả lời cho đến nay trả lời câu hỏi này cho các ngôn ngữ cụ thể và trong một số trường hợp không nêu rõ họ đang nói về ngôn ngữ nào.
James McNellis

2
Xấu là một thuật ngữ tương đối. Tránh truyền là cách tốt nhất, nhưng đôi khi một lập trình viên phải làm những gì mà một lập trình viên phải làm. (đặc biệt nếu bạn viết một chương trình java 1.5+ sử dụng thư viện được viết cho 1.4) Có lẽ nên đổi tên câu hỏi, "Tại sao nên tránh truyền?"
Mike Miller

2
Đây là một câu hỏi tuyệt vời .... Nó đã được đóng bởi 5 người dùng, mỗi người có ít hơn 10k danh tiếng !! Rõ ràng là một cách sử dụng quá mức các sức mạnh mới. Đã bỏ phiếu để mở lại.
reach4thelasers

Câu trả lời:


141

Bạn đã gắn thẻ điều này với ba ngôn ngữ và câu trả lời thực sự khá khác nhau giữa ba ngôn ngữ. Thảo luận về C ++ ít nhiều cũng ngụ ý thảo luận về các phôi C, và điều đó mang lại (ít nhiều) câu trả lời thứ tư.

Vì đó là vấn đề mà bạn không đề cập rõ ràng, tôi sẽ bắt đầu với C. C casts có một số vấn đề. Một là họ có thể làm bất kỳ một số việc khác nhau. Trong một số trường hợp, dàn diễn viên không làm gì khác hơn là nói với trình biên dịch (về bản chất): "Im đi, tôi biết tôi đang làm gì" - tức là, nó đảm bảo rằng ngay cả khi bạn thực hiện một chuyển đổi có thể gây ra sự cố, trình biên dịch sẽ không cảnh báo bạn về những vấn đề tiềm ẩn đó. Ví dụ char a=(char)123456;,. Kết quả chính xác của việc triển khai này được xác định (phụ thuộc vào quy mô và tính ký kết củachar), và ngoại trừ trong những tình huống khá lạ, có lẽ không hữu ích. Các phôi C cũng khác nhau ở việc chúng là thứ chỉ xảy ra tại thời điểm biên dịch (tức là bạn chỉ nói với trình biên dịch cách diễn giải / xử lý một số dữ liệu) hay điều gì đó xảy ra tại thời điểm chạy (ví dụ: một chuyển đổi thực tế từ gấp đôi sang Dài).

C ++ cố gắng giải quyết vấn đề đó ở một mức độ nào đó bằng cách thêm một số toán tử ép kiểu "mới", mỗi toán tử được giới hạn ở chỉ một tập hợp con các khả năng của ép kiểu C. Điều này làm cho việc (ví dụ) vô tình thực hiện một chuyển đổi mà bạn thực sự không có ý định trở nên khó khăn hơn - nếu bạn chỉ có ý định loại bỏ hằng số trên một đối tượng, bạn có thể sử dụng const_castvà đảm bảo rằng điều duy nhất nó có thể ảnh hưởng là liệu một đối tượng là const, volatilehay không. Ngược lại, một static_castkhông được phép ảnh hưởng đến việc một đối tượng là consthayvolatile. Tóm lại, bạn có hầu hết các loại khả năng giống nhau, nhưng chúng được phân loại để một kiểu truyền thông thường chỉ có thể thực hiện một loại chuyển đổi, trong đó một kiểu truyền kiểu C có thể thực hiện hai hoặc ba chuyển đổi trong một thao tác. Ngoại lệ chính là bạn có thể sử dụng dynamic_castthay cho a static_casttrong ít nhất một số trường hợp và mặc dù được viết là a dynamic_cast, nhưng nó thực sự sẽ kết thúc là a static_cast. Ví dụ: bạn có thể sử dụng dynamic_castđể truyền lên hoặc xuống một hệ thống phân cấp - nhưng việc ép kiểu "lên" hệ thống phân cấp luôn an toàn, vì vậy nó có thể được thực hiện tĩnh, trong khi việc truyền "xuống" hệ thống phân cấp không nhất thiết phải an toàn vì vậy nó được thực hiện động.

Java và C # giống nhau hơn nhiều. Đặc biệt, với cả hai, việc đúc (hầu như?) Luôn là một hoạt động thời gian chạy. Xét về các toán tử ép kiểu C ++, nó thường gần nhất với a dynamic_castvề những gì thực sự được thực hiện - tức là, khi bạn cố gắng truyền một đối tượng sang một số loại đích, trình biên dịch sẽ chèn một kiểm tra thời gian chạy để xem liệu chuyển đổi đó có được phép không và ném một ngoại lệ nếu không. Các chi tiết chính xác (ví dụ: tên được sử dụng cho ngoại lệ "bad cast") khác nhau, nhưng nguyên tắc cơ bản hầu như vẫn tương tự (mặc dù, nếu bộ nhớ phục vụ, Java thực hiện các phôi áp dụng cho một số kiểu không phải đối tượng như intgần với C hơn nhiều phôi - nhưng những loại này hiếm khi được sử dụng đủ đến mức 1) Tôi không nhớ điều đó chắc chắn, và 2) ngay cả khi nó đúng, nó không quan trọng lắm).

Nhìn vào mọi thứ một cách tổng quát hơn, tình huống khá đơn giản (ít nhất là IMO): một dàn diễn viên (rõ ràng là đủ) có nghĩa là bạn đang chuyển đổi thứ gì đó từ loại này sang loại khác. Khi / nếu bạn làm điều đó, nó đặt ra câu hỏi "Tại sao?" Nếu bạn thực sự muốn một cái gì đó là một loại cụ thể, tại sao bạn không xác định nó là loại đó để bắt đầu? Điều đó không có nghĩa là không bao giờ có lý do để thực hiện chuyển đổi như vậy, nhưng bất cứ khi nào nó xảy ra, nó sẽ gợi lên câu hỏi liệu bạn có thể thiết kế lại mã để loại đúng được sử dụng xuyên suốt hay không. Ngay cả những chuyển đổi có vẻ vô hại (ví dụ: giữa số nguyên và dấu phẩy động) cũng nên được kiểm tra kỹ hơn nhiều so với mức bình thường. Bất chấp vẻ ngoài của họtương tự, số nguyên thực sự nên được sử dụng cho các loại sự vật "được đếm" và dấu phẩy động cho các loại thứ "được đo lường". Bỏ qua sự phân biệt là điều dẫn đến một số tuyên bố điên rồ như "một gia đình Mỹ trung bình có 1,8 đứa con." Mặc dù tất cả chúng ta đều có thể thấy điều đó xảy ra như thế nào, nhưng thực tế là không có gia đình nào có 1,8 con. Họ có thể có 1 hoặc có thể có 2 hoặc họ có thể có nhiều hơn thế - nhưng không bao giờ có 1.8.


10
Có vẻ như bạn đang bị 'chết bởi khoảng chú ý' ở đây, đây là một câu trả lời hay đấy imo.
Steve Townsend

2
Một câu trả lời chắc chắn, nhưng một số phần "Tôi không biết" có thể được thắt chặt lại để làm cho câu trả lời toàn diện. @ Dragontamer5788 đó là một câu trả lời tốt, nhưng không toàn diện.
M2tM

5
Một trẻ cả và một trẻ bị cụt chân sẽ là 1,8 trẻ. Nhưng câu trả lời rất hay không hơn không kém. :)
Oz.

1
Một câu trả lời rất hay không bị cản trở ít nhất là loại trừ các cặp vợ chồng không có con.

@Roger: không loại trừ, chỉ bỏ qua.
Jerry Coffin

48

Rất nhiều câu trả lời hay ở đây. Đây là cách tôi nhìn nó (từ góc độ C #).

Đúc thường có nghĩa là một trong hai điều:

  • Tôi biết kiểu thời gian chạy của biểu thức này nhưng trình biên dịch không biết nó. Trình biên dịch, tôi đang nói với bạn, trong thời gian chạy, đối tượng tương ứng với biểu thức này thực sự sẽ thuộc loại này. Đến bây giờ, bạn biết rằng biểu hiện này được coi là thuộc loại này. Tạo mã giả định rằng đối tượng sẽ thuộc loại đã cho hoặc, ném một ngoại lệ nếu tôi sai.

  • Cả trình biên dịch và nhà phát triển đều biết kiểu thời gian chạy của biểu thức. Có một giá trị khác thuộc kiểu khác được liên kết với giá trị mà biểu thức này sẽ có trong thời gian chạy. Tạo mã tạo ra giá trị của kiểu mong muốn từ giá trị của kiểu đã cho; nếu bạn không thể làm như vậy, hãy ném một ngoại lệ.

Lưu ý rằng đó là những điều đối lập . Có hai loại phôi! Có những phôi mà bạn đang đưa ra gợi ý cho trình biên dịch về thực tế - này, đối tượng kiểu này thực sự thuộc loại Khách hàng - và có những phôi mà bạn đang yêu cầu trình biên dịch thực hiện ánh xạ từ kiểu này sang kiểu khác - này, Tôi cần số int tương ứng với đôi này.

Cả hai loại phôi đều có màu đỏ cờ. Loại diễn viên đầu tiên đặt ra câu hỏi "tại sao chính xác là nhà phát triển biết điều gì đó mà trình biên dịch không?" Nếu bạn đang ở trong hoàn cảnh đó thì điều tốt hơn để làm thường là thay đổi chương trình để trình biên dịch không có một xử lý trên thực tế. Sau đó, bạn không cần diễn viên; phân tích được thực hiện tại thời điểm biên dịch.

Kiểu ép kiểu thứ hai đặt ra câu hỏi "tại sao thao tác không được thực hiện trong kiểu dữ liệu đích ngay từ đầu?" Nếu bạn cần một kết quả là int thì tại sao bạn lại giữ một cú đúp ngay từ đầu? Bạn không nên giữ một int?

Một số suy nghĩ bổ sung ở đây:

http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/


2
Tôi không nghĩ đó là những lá cờ đỏ vốn có. Nếu bạn có một Foođối tượng kế thừa từ Barvà bạn lưu trữ đối tượng đó trong a List<Bar>, thì bạn sẽ cần phôi nếu muốn Foolấy lại. Có lẽ nó chỉ ra một vấn đề ở cấp độ kiến ​​trúc (tại sao chúng ta lưu trữ Bars thay vì Foos?), Nhưng không nhất thiết. Và, nếu điều đó Foocũng có một kết hợp hợp lệ int, nó cũng giải quyết nhận xét khác của bạn: Bạn đang lưu trữ a Foo, không phải an int, bởi vì an intkhông phải lúc nào cũng thích hợp.
Mike Caron

6
@Mike Caron - Tôi không thể trả lời cho Eric, rõ ràng, nhưng với tôi lá cờ đỏ có nghĩa là "đây là điều cần suy nghĩ", không phải "đây là điều gì đó sai". Và không có gì khó khăn khi lưu trữ a Footrong a List<Bar>, nhưng tại thời điểm diễn viên, bạn đang cố gắng làm điều gì đó với Foođiều đó không phù hợp với a Bar. Điều đó có nghĩa là hành vi khác nhau cho các kiểu con đang được thực hiện thông qua một cơ chế khác với tính đa hình tích hợp được cung cấp bởi các phương thức ảo. Có lẽ đó là giải pháp đúng, nhưng thường thì đó là một lá cờ đỏ.
Jeffrey L Whitledge

13
Thật. Nếu bạn đang kéo mọi thứ ra khỏi danh sách động vật và sau đó bạn cần phải nói với trình biên dịch, ồ, nhân tiện, tôi tình cờ biết rằng con đầu tiên là hổ, con thứ hai là sư tử và con thứ ba là một con gấu, thì bạn nên sử dụng Tuple <Lion, Tiger, Bear>, chứ không phải là List <Animal>.
Eric Lippert

5
Tôi đồng ý với bạn, nhưng tôi nghĩ rằng nếu không có hỗ trợ cú pháp tốt hơn cho các Tuple<X,Y,...>bộ giá trị thì khó có thể thấy việc sử dụng rộng rãi trong C #. Đây là một nơi mà ngôn ngữ có thể làm tốt hơn công việc thúc đẩy mọi người tiến tới "hố sâu của thành công".
kvb

4
@kvb: Tôi đồng ý. Chúng tôi đã cân nhắc việc áp dụng cú pháp tuple cho C # 4 nhưng nó không phù hợp với ngân sách. Có lẽ trong C # 5; chúng tôi chưa tìm ra bộ tính năng đầy đủ. Quá bận rộn với việc tập hợp các CTP để không đồng bộ hóa. Hoặc có lẽ trong một phiên bản giả định trong tương lai.
Eric Lippert

36

Lỗi truyền luôn được báo cáo là lỗi thời gian chạy trong java. Việc sử dụng generic hoặc templating biến những lỗi này thành lỗi thời gian biên dịch, giúp bạn dễ dàng phát hiện khi bạn mắc lỗi hơn nhiều.

Như tôi đã nói ở trên. Điều này không có nghĩa là tất cả quá trình casting đều tệ. Nhưng nếu có thể tránh được thì tốt nhất bạn nên làm như vậy.


4
Điều này giả định rằng tất cả quá trình đúc là "xấu"; tuy nhiên, điều này không hoàn toàn đúng. Lấy C #, chẳng hạn, với cả hỗ trợ ép kiểu ngầm và rõ ràng. Vấn đề xảy ra khi thực hiện ép kiểu (vô tình) loại bỏ thông tin hoặc kiểu an toàn (điều này khác nhau tùy theo ngôn ngữ).

5
Trên thực tế, điều này là sai trong C ++. Có lẽ một chỉnh sửa để bao gồm (các) ngôn ngữ được nhắm mục tiêu với thông tin này là theo thứ tự.
Steve Townsend

@Erick - Tôi muốn, nhưng tôi không biết điều đầu tiên về Java, cũng như không đủ chi tiết về C # để đảm bảo thông tin là chính xác.
Steve Townsend

Xin lỗi, tôi là dân Java, vì vậy kiến ​​thức c ++ của tôi có hạn. Tôi có thể chỉnh sửa từ "tạo mẫu" ra khỏi nó, vì nó được dự định là mơ hồ.
Mike Miller

Không phải vậy. Một số lỗi ép kiểu được trình biên dịch Java phát hiện và không chỉ là những lỗi chung chung. Xét (Chuỗi) 0;
Marquis of Lorne

18

Casting vốn dĩ không phải là xấu, chỉ là nó thường bị lạm dụng như một phương tiện để đạt được điều gì đó thực sự không nên làm, hoặc làm một cách thanh lịch hơn.

Nếu nó tệ trên toàn cầu, các ngôn ngữ sẽ không hỗ trợ nó. Giống như bất kỳ tính năng ngôn ngữ nào khác, nó có vị trí của nó.

Lời khuyên của tôi là tập trung vào ngôn ngữ chính của bạn và hiểu tất cả các cấu trúc của nó cũng như các phương pháp hay nhất liên quan. Điều đó sẽ thông báo các chuyến du ngoạn sang các ngôn ngữ khác.

Các tài liệu C # liên quan có ở đây .

Có một bản tóm tắt tuyệt vời về các tùy chọn C ++ tại một câu hỏi SO trước đây .


9

Tôi chủ yếu nói về C ++ ở đây, nhưng hầu hết điều này có thể áp dụng cho Java và C #:

C ++ là một ngôn ngữ được định kiểu tĩnh . Có một số thời gian mà ngôn ngữ cho phép bạn thực hiện điều này (hàm ảo, chuyển đổi ngầm định), nhưng về cơ bản trình biên dịch biết loại của mọi đối tượng tại thời điểm biên dịch. Lý do để sử dụng một ngôn ngữ như vậy là các lỗi có thể được phát hiện tại thời điểm biên dịch . Nếu trình biên dịch biết các loại ab, thì nó sẽ bắt bạn tại thời điểm biên dịch khi bạn thực hiện a=bđâu alà số phức và blà một chuỗi.

Bất cứ khi nào bạn thực hiện truyền rõ ràng, bạn sẽ yêu cầu trình biên dịch đóng cửa, vì bạn nghĩ rằng bạn biết rõ hơn . Trong trường hợp bạn sai, bạn thường sẽ chỉ phát hiện ra khi chạy . Và vấn đề với việc tìm ra trong thời gian chạy là điều này có thể xảy ra ở khách hàng.


5

Java, c # và c ++ là các ngôn ngữ được gõ mạnh, mặc dù các ngôn ngữ được gõ mạnh có thể được coi là không linh hoạt, chúng có lợi ích là thực hiện kiểm tra kiểu tại thời điểm biên dịch và bảo vệ bạn khỏi các lỗi thời gian chạy do nhập sai kiểu cho các hoạt động nhất định.

Cơ bản có hai loại phôi: đúc cho một loại chung chung hơn hoặc đúc cho một loại khác (cụ thể hơn). Truyền tới một kiểu tổng quát hơn (truyền sang kiểu mẹ) sẽ giữ nguyên các kiểm tra thời gian biên dịch. Nhưng truyền sang các kiểu khác (các kiểu cụ thể hơn) sẽ vô hiệu hóa kiểm tra kiểu thời gian biên dịch và sẽ được trình biên dịch thay thế bằng kiểm tra thời gian chạy. Điều này có nghĩa là bạn không chắc chắn rằng mã đã biên dịch sẽ chạy chính xác. Nó cũng có một số tác động đến hiệu suất không đáng kể, do phải kiểm tra thêm kiểu thời gian chạy (API Java có đầy các phôi!).


5
Không phải tất cả các kiểu "bỏ qua" đều an toàn trong C ++.
James McNellis

4
Không phải tất cả các phôi "bỏ qua" đều loại an toàn trong C #.

5
Không phải tất cả các phôi kiểu "bỏ qua" đều an toàn trong Java.
Erick Robertson

11
Không phải tất cả phôi "bỏ qua" đều an toàn trong Truyền. Oh chờ đợi, tag mà không đề cập đến một ngôn ngữ ...
SBI

2
Trong hầu hết các trường hợp trong C # và Java, việc ép kiểu sẽ khiến bạn giảm hiệu suất, vì hệ thống sẽ thực hiện kiểm tra kiểu thời gian chạy (không miễn phí). Trong C ++, dynamic_castnói chung là chậm hơn static_cast, vì nó thường phải thực hiện kiểm tra kiểu thời gian chạy (với một số lưu ý: truyền đến một kiểu cơ sở là rẻ, v.v.).
Travis Gockel

4

Một số kiểu đúc rất an toàn và hiệu quả đến mức thường không được coi là đúc.

Nếu bạn truyền từ kiểu dẫn xuất sang kiểu cơ sở, điều này nói chung là khá rẻ (thường - tùy thuộc vào ngôn ngữ, cách triển khai và các yếu tố khác - nó là không có chi phí) và an toàn.

Nếu bạn ép kiểu đơn giản như kiểu int sang kiểu rộng hơn như kiểu int dài, thì một lần nữa, nó thường khá rẻ (thường không đắt hơn nhiều so với việc gán cùng kiểu với kiểu ép đó) và một lần nữa là an toàn.

Các loại khác đầy hơn và / hoặc đắt hơn. Trong hầu hết các ngôn ngữ, việc truyền từ kiểu cơ sở sang kiểu dẫn xuất là rẻ nhưng có nguy cơ cao bị lỗi nghiêm trọng (trong C ++ nếu bạn static_cast từ cơ sở sang kiểu dẫn xuất thì nó sẽ rẻ, nhưng nếu giá trị cơ bản không thuộc kiểu dẫn xuất thì hành vi là không xác định và có thể rất lạ) hoặc tương đối đắt tiền và có nguy cơ tạo ra một ngoại lệ (dynamic_cast trong C ++, ép kiểu cơ sở đến dẫn xuất rõ ràng trong C #, v.v.). Quyền anh trong Java và C # là một ví dụ khác về điều này, và một khoản chi phí thậm chí còn lớn hơn (xem xét rằng chúng đang thay đổi nhiều hơn là chỉ cách các giá trị cơ bản được xử lý).

Các kiểu ép kiểu khác có thể làm mất thông tin (kiểu số nguyên dài thành kiểu số nguyên ngắn).

Những trường hợp rủi ro này (cho dù là ngoại lệ hay lỗi nghiêm trọng hơn) và chi phí đều là những lý do để tránh sử dụng.

Một lý do mang tính khái niệm hơn, nhưng có lẽ quan trọng hơn, là mỗi trường hợp truyền là một trường hợp mà khả năng suy luận về tính đúng đắn của mã của bạn bị hạn chế: Mỗi trường hợp là một nơi khác, nơi có thể xảy ra lỗi và cách thức diễn ra có thể sai làm tăng thêm sự phức tạp của việc suy luận xem toàn bộ hệ thống có bị sai hay không. Ngay cả khi dàn diễn viên được chứng minh là an toàn mỗi lần, chứng tỏ đây là một phần bổ sung của lý do.

Cuối cùng, việc sử dụng nhiều phôi có thể cho thấy việc không xem xét tốt mô hình đối tượng trong việc tạo, sử dụng nó hoặc cả hai: Truyền qua lại giữa một vài kiểu thường xuyên hầu như luôn luôn không xem xét mối quan hệ giữa các kiểu đã sử dụng. Đây không phải là quá nhiều cho rằng phôi là xấu, vì chúng là một dấu hiệu của một cái gì đó xấu.


2

Ngày càng có xu hướng các lập trình viên bám vào các quy tắc giáo điều về việc sử dụng các tính năng ngôn ngữ ("không bao giờ sử dụng XXX!", "XXX bị coi là có hại", v.v.), trong đó XXX phạm vi từ gotos đến con trỏ đến protectedcác thành viên dữ liệu đến các thẻ đơn để chuyển các đối tượng qua giá trị.

Theo kinh nghiệm của tôi, tuân theo các quy tắc như vậy đảm bảo hai điều: bạn sẽ không phải là một lập trình viên tồi, cũng không phải là một lập trình viên giỏi.

Một cách tiếp cận tốt hơn là để đào xuống và phát hiện ra những hạt nhân của sự thật đằng sau những điều cấm chăn, và sau đó sử dụng các tính năng một cách sáng suốt, với sự hiểu biết rằng có rất nhiều tình huống mà họ là những công cụ tốt nhất cho công việc.

"Tôi thường tránh các kiểu đúc càng nhiều càng tốt" là một ví dụ điển hình về quy tắc tổng quát hóa quá mức như vậy. Phôi rất cần thiết trong nhiều tình huống phổ biến. Vài ví dụ:

  1. Khi tương tác với mã của bên thứ ba (đặc biệt là khi mã đó chứa đầy typedefs). (Ví dụ: GLfloat<--> double<--> Real.)
  2. Truyền từ một con trỏ / tham chiếu dẫn xuất đến lớp cơ sở: Điều này rất phổ biến và tự nhiên nên trình biên dịch sẽ thực hiện nó một cách ngầm định. Nếu làm cho nó rõ ràng làm tăng khả năng đọc, thì diễn viên là một bước tiến chứ không phải lùi!
  3. Truyền từ cơ sở đến con trỏ / tham chiếu lớp dẫn xuất: Cũng phổ biến, ngay cả trong mã được thiết kế tốt. (Ví dụ: các thùng chứa không đồng nhất.)
  4. Bên trong tuần tự hóa / giải mã hóa nhị phân hoặc mã cấp thấp khác cần truy cập vào các byte thô của các loại tích hợp sẵn.
  5. Bất cứ lúc nào khi bạn chỉ cần sử dụng một kiểu khác sẽ tự nhiên, thuận tiện và dễ đọc hơn. (Ví dụ: std::size_type-> int.)

Chắc chắn có nhiều trường hợp không thích hợp để sử dụng diễn viên, và điều quan trọng là phải học những điều này; Tôi sẽ không đi vào quá nhiều chi tiết vì các câu trả lời ở trên đã làm rất tốt việc chỉ ra một số trong số chúng.


1

Để giải thích rõ hơn về câu trả lời của KDeveloper , nó không phải là kiểu an toàn. Với tính năng truyền, không có gì đảm bảo rằng những gì bạn đang truyền và truyền đến sẽ khớp với nhau và nếu điều đó xảy ra, bạn sẽ nhận được ngoại lệ thời gian chạy, điều này luôn là một điều tồi tệ.

Liên quan cụ thể đến C #, bởi vì nó bao gồm các toán tử isas, bạn có cơ hội (phần lớn) đưa ra quyết định xem liệu một phép ép kiểu có thành công hay không. Do đó, bạn nên thực hiện các bước thích hợp để xác định liệu hoạt động có thành công hay không và tiến hành một cách thích hợp.


1

Trong trường hợp C #, người ta cần phải cẩn thận hơn trong khi truyền vì chi phí quyền anh / unboxing liên quan trong khi xử lý các loại giá trị.


1

Không chắc ai đó đã đề cập đến vấn đề này chưa, nhưng trong C # ép kiểu có thể được sử dụng theo cách khá an toàn và thường cần thiết. Giả sử bạn nhận được một đối tượng có thể có nhiều loại. Sử dụng istừ khóa trước tiên, bạn có thể xác nhận rằng đối tượng thực sự thuộc loại mà bạn định truyền nó sang, sau đó truyền trực tiếp đối tượng sang loại đó. (Tôi không làm việc với Java nhiều nhưng tôi chắc chắn rằng có một cách rất đơn giản để làm điều đó ở đó).


1

Bạn chỉ truyền một đối tượng đến một số kiểu, nếu 2 điều kiện được đáp ứng:

  1. bạn biết nó thuộc loại đó
  2. trình biên dịch không

Điều này có nghĩa là không phải tất cả thông tin bạn có đều được trình bày tốt trong cấu trúc loại bạn sử dụng. Điều này thật tệ, bởi vì việc triển khai của bạn phải bao gồm mô hình của bạn về mặt ngữ nghĩa , điều này rõ ràng là không trong trường hợp này.

Bây giờ khi bạn thực hiện cast, điều này có thể có 2 lý do khác nhau:

  1. Bạn đã làm một công việc không tốt trong việc thể hiện các mối quan hệ kiểu.
  2. hệ thống loại ngôn ngữ đơn giản là không đủ biểu cảm để diễn đạt chúng.

Trong hầu hết các ngôn ngữ, bạn gặp phải tình huống thứ hai rất nhiều lần. Generics như trong Java giúp ích một chút, hệ thống khuôn mẫu C ++ thậm chí còn nhiều hơn, nhưng rất khó để làm chủ và thậm chí sau đó một số thứ có thể là không thể hoặc không đáng để nỗ lực.

Vì vậy, bạn có thể nói, diễn viên là một thủ thuật bẩn thỉu nhằm phá vỡ các vấn đề của bạn để diễn đạt một số mối quan hệ kiểu cụ thể bằng một số ngôn ngữ cụ thể. Cần tránh hack bẩn. Nhưng bạn không bao giờ có thể sống mà không có chúng.


0

Nói chung, các mẫu (hoặc thuốc chung) là loại an toàn hơn phôi. Về mặt đó, tôi muốn nói rằng một vấn đề với quá trình truyền là an toàn kiểu. Tuy nhiên, có một vấn đề khác phức tạp hơn đặc biệt liên quan đến dự báo giảm: thiết kế. Theo quan điểm của tôi, ít nhất, dự báo xuống là một mùi mã, một dấu hiệu cho thấy có điều gì đó không ổn với mô tả của tôi và tôi nên điều tra thêm. Tại sao lại đơn giản: nếu bạn "hiểu" đúng những điều trừu tượng, bạn chỉ đơn giản là không cần nó! Nhân tiện, câu hỏi rất hay ...

Chúc mừng!


0

Nói một cách thực sự ngắn gọn, lý do chính đáng là vì tính di động. Kiến trúc khác nhau mà cả hai đều sử dụng cùng một ngôn ngữ có thể có các int có kích thước khác nhau. Vì vậy, nếu tôi di chuyển từ ArchA sang ArchB, có int hẹp hơn, tốt nhất là tôi có thể thấy hành vi kỳ quặc và tệ nhất là gặp lỗi.

(Tôi rõ ràng đang bỏ qua bytecode và IL độc lập về kiến ​​trúc.)

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.