Một cách sử dụng đúng của downcasting là gì?


67

Downcasting có nghĩa là truyền từ một lớp cơ sở (hoặc giao diện) sang một lớp con hoặc lớp lá.

Một ví dụ về downcast có thể là nếu bạn truyền từ System.Objectmột số loại khác.

Downcasting không phổ biến, có thể là mùi mã: Học thuyết hướng đối tượng là thích, ví dụ, xác định và gọi các phương thức ảo hoặc trừu tượng thay vì downcasting.

  • Điều gì, nếu có, là trường hợp sử dụng tốt và thích hợp cho downcasting? Đó là, trong trường hợp nào là thích hợp để viết mã mà phát sóng?
  • Nếu câu trả lời của bạn là "không", thì tại sao ngôn ngữ này lại được hỗ trợ bởi ngôn ngữ?

5
Những gì bạn mô tả thường được gọi là "downcasting". Được trang bị kiến ​​thức đó, trước tiên bạn có thể thực hiện một số nghiên cứu thêm và giải thích lý do tại sao bạn tìm thấy không đủ? TL; DR: downcasting phá vỡ kiểu gõ tĩnh, nhưng đôi khi một hệ thống kiểu quá hạn chế. Vì vậy, thật hợp lý khi một ngôn ngữ cung cấp dịch vụ giảm âm an toàn.
amon

Bạn có nghĩa là u ám? Upcasting sẽ được tăng lên thông qua hệ thống phân cấp lớp, tức là từ lớp con đến siêu lớp, trái ngược với việc hạ âm, từ siêu lớp đến lớp con ...
Andrei Socaciu

Tôi xin lỗi: bạn nói đúng. Tôi đã chỉnh sửa câu hỏi để đổi tên "upcast" thành "downcast".
ChrisW

@amon en.wikipedia.org/wiki/Downcasting đưa ra "convert to string" làm ví dụ (.Net hỗ trợ sử dụng ảo ToString); ví dụ khác của nó là "các thùng chứa Java" vì Java không hỗ trợ chung chung (mà C # làm). stackoverflow.com/questions/1524197/downcast-and-upcast cho biết downcasting gì , nhưng không có ví dụ về thời điểm thích hợp .
ChrisW

4
@ChrisW Đó before Java had support for genericskhông phải because Java doesn't support generics. Và amon khá nhiều đã cho bạn câu trả lời - khi hệ thống loại bạn đang làm việc quá hạn chế và bạn không ở vị trí để thay đổi nó.
Thường

Câu trả lời:


12

Dưới đây là một số cách sử dụng thích hợp của downcasting.

Và tôi tôn trọng kịch liệt không đồng ý với những người khác ở đây nói rằng việc sử dụng các chương trình truyền phát chắc chắn là một mùi mã, bởi vì tôi tin rằng không có cách hợp lý nào khác để giải quyết các vấn đề tương ứng.

Equals:

class A
{
    // Nothing here, but if you're a C++ programmer who dislikes object.Equals(object),
    // pretend it's not there and we have abstract bool A.Equals(A) instead.
}
class B : A
{
    private int x;
    public override bool Equals(object other)
    {
        var casted = other as B;  // cautious downcast (dynamic_cast in C++)
        return casted != null && this.x == casted.x;
    }
}

Clone:

class A : ICloneable
{
    // Again, if you dislike ICloneable, that's not the point.
    // Just ignore that and pretend this method returns type A instead of object.
    public virtual object Clone()
    {
        return this.MemberwiseClone();  // some sane default behavior, whatever
    }
}
class B : A
{
    private int[] x;
    public override object Clone()
    {
        var copy = (B)base.Clone();  // known downcast (static_cast in C++)
        copy.x = (int[])this.x.Clone();  // oh hey, another downcast!!
        return copy;
    }
}

Stream.EndRead/Write:

class MyStream : Stream
{
    private class AsyncResult : IAsyncResult
    {
        // ...
    }
    public override int EndRead(IAsyncResult other)
    {
        return Blah((AsyncResult)other);  // another downcast (likely ~static_cast)
    }
}

Nếu bạn định nói đây là những mùi mã, bạn cần cung cấp các giải pháp tốt hơn cho chúng (ngay cả khi chúng bị tránh do sự bất tiện). Tôi không tin giải pháp tốt hơn tồn tại.


9
"Mùi mã" không có nghĩa là "thiết kế này chắc chắnxấu " mà nó có nghĩa là "thiết kế này đáng ngờ ". Rằng có những đặc điểm ngôn ngữ vốn đáng nghi ngờ là vấn đề thực dụng từ phía tác giả ngôn ngữ
Caleth

5
@Caleth: Và toàn bộ quan điểm của tôi là những thiết kế này xuất hiện mọi lúc, và hoàn toàn không có gì đáng nghi ngờ về các thiết kế tôi đã trình bày, và do đó việc đúc không phải là mùi mã.
Mehrdad

4
@Caleth tôi không đồng ý. Đối với tôi, mùi mã có nghĩa là mã xấu hoặc ít nhất có thể được cải thiện.
Konrad Rudolph

6
@Mehrdad Ý kiến ​​của bạn dường như là mùi mã phải là lỗi của các lập trình viên ứng dụng. Tại sao? Không phải mọi mùi mã phải là một vấn đề thực tế hoặc có một giải pháp. Mùi mã theo Martin Fowler chỉ đơn giản là "một dấu hiệu bề mặt thường tương ứng với một vấn đề sâu hơn trong hệ thống" object.Equalsphù hợp với mô tả đó khá tốt (cố gắng cung cấp chính xác một hợp đồng bằng trong một siêu lớp cho phép các lớp con thực hiện chính xác - đó là hoàn toàn không tầm thường). Vì các lập trình viên ứng dụng chúng ta không thể giải quyết nó (trong một số trường hợp chúng ta có thể tránh nó) là một chủ đề khác.
Voo

5
@Mehrdad Hãy xem một trong những nhà thiết kế ngôn ngữ gốc nói gì về Equals .. Object.Equals is horrid. Equality computation in C# is completely messed up(Nhận xét của Eric về câu trả lời của anh ấy). Thật buồn cười khi bạn không muốn xem xét lại ý kiến ​​của mình ngay cả khi một trong những nhà thiết kế chính của ngôn ngữ nói với bạn rằng bạn đã sai.
Voo

135

Downcasting là không phổ biến, có thể là một mùi mã

Tôi không đồng ý. Downcasting là cực kỳ phổ biến ; một số lượng lớn các chương trình trong thế giới thực chứa một hoặc nhiều chương trình phát sóng. Và nó có thể không phải một mùi mã. Nó chắc chắn là một mùi mã. Đó là lý do tại sao hoạt động downcasting được yêu cầu phải được hiển thị trong văn bản của chương trình . Nó để bạn có thể dễ dàng nhận thấy mùi hơn và dành sự chú ý đánh giá mã về nó.

Trong trường hợp nào thì thích hợp để viết mã mà phát sóng?

Trong mọi trường hợp:

  • bạn có kiến thức đúng 100% về một thực tế về loại thời gian chạy của một biểu thức cụ thể hơn loại thời gian biên dịch của biểu thức và
  • bạn cần tận dụng thực tế đó để sử dụng khả năng của đối tượng không khả dụng trên loại thời gian biên dịch và
  • sử dụng thời gian và công sức để viết dàn diễn viên tốt hơn là tái cấu trúc chương trình để loại bỏ một trong hai điểm đầu tiên.

Nếu bạn có thể cấu trúc lại một chương trình một cách rẻ tiền để loại thời gian chạy có thể được suy ra bởi trình biên dịch hoặc để cấu trúc lại chương trình để bạn không cần khả năng của loại dẫn xuất hơn, thì hãy làm như vậy. Truyền phát đã được thêm vào ngôn ngữ cho những trường hợp khó khăntốn kém để tái cấu trúc chương trình.

Tại sao downcasting được hỗ trợ bởi ngôn ngữ?

C # được phát minh bởi các lập trình viên thực dụng, những người có việc phải làm, cho các lập trình viên thực dụng, những người có việc phải làm. Các nhà thiết kế C # không phải là những người theo chủ nghĩa thuần túy OO. Và hệ thống loại C # không hoàn hảo; bằng cách thiết kế nó đánh giá thấp các hạn chế có thể được đặt trên loại thời gian chạy của một biến.

Ngoài ra, downcasting rất an toàn trong C #. Chúng tôi có một đảm bảo chắc chắn rằng downcast sẽ được xác minh trong thời gian chạy và nếu nó không thể được xác minh thì chương trình sẽ thực hiện đúng và gặp sự cố. Đây là điều tuyệt vời; điều đó có nghĩa là nếu sự hiểu biết đúng 100% về ngữ nghĩa của bạn hóa ra đúng 99,99%, thì chương trình của bạn hoạt động 99,99% thời gian và làm hỏng phần còn lại của thời gian, thay vì hành xử không thể đoán trước và làm hỏng dữ liệu người dùng 0,01% thời gian .


BÀI TẬP: Có ít nhất một cách để tạo ra một downcast trong C # mà không cần sử dụng toán tử cast rõ ràng. Bạn có thể nghĩ về bất kỳ kịch bản như vậy? Vì đây cũng là những mùi mã tiềm năng, bạn nghĩ yếu tố thiết kế nào đã đi vào thiết kế của một tính năng có thể tạo ra sự cố downcast mà không có biểu hiện trong mã?


101
Hãy nhớ những áp phích ở trường trung học trên tường "Điều gì phổ biến không phải lúc nào cũng đúng, điều gì đúng không phải lúc nào cũng phổ biến?" Bạn nghĩ rằng họ đang cố gắng ngăn bạn khỏi ma túy hoặc mang thai ở trường trung học nhưng họ thực sự đã cảnh báo về sự nguy hiểm của nợ kỹ thuật.
corsiKa

14
@EricLippert, tuyên bố foreach, tất nhiên. Điều này đã được thực hiện trước khi IEnumerable là chung chung, phải không?
Arturo Torres Sánchez

18
Lời giải thích này làm cho ngày của tôi. Là một giáo viên, tôi thường được hỏi tại sao C # được thiết kế theo cách này hay cách khác và câu trả lời của tôi thường dựa trên động lực mà bạn và đồng nghiệp của bạn đã chia sẻ ở đây và trên blog của bạn. Việc trả lời câu hỏi tiếp theo thường khó hơn một chút "Nhưng tại saoyyyy ?!". Và ở đây tôi tìm thấy câu trả lời tiếp theo hoàn hảo: "C # được phát minh bởi các lập trình viên thực dụng, những người có công việc phải làm, dành cho các lập trình viên thực dụng, có công việc phải làm.". :-) Cảm ơn bạn @EricLippert!
Zano

12
Bên cạnh: Rõ ràng trong nhận xét của tôi ở trên tôi có nghĩa là để nói rằng chúng ta có một nhìn xuống từ A đến B. Tôi thực sự không thích việc sử dụng "lên" và "xuống" và "mẹ" và "con" khi điều hướng một hệ thống phân cấp lớp và tôi nhận được chúng sai tất cả thời gian trong văn bản của tôi. Mối quan hệ giữa các loại là mối quan hệ siêu bộ / tập hợp con, vậy tại sao nên có một hướng lên hoặc xuống liên quan đến nó, tôi không biết. Và thậm chí đừng để tôi bắt đầu với "cha mẹ". Cha mẹ của hươu cao cổ không phải là động vật có vú, cha mẹ của hươu cao cổ là ông bà Gi hươu cao cổ.
Eric Lippert

9
Object.Equalskhủng khiếp . Tính toán bình đẳng trong C # hoàn toàn sai lầm , quá sai lầm để giải thích trong một bình luận. Có rất nhiều cách để thể hiện sự bình đẳng; nó rất dễ làm cho chúng không nhất quán; nó là rất dễ dàng, trên thực tế cần thiết để vi phạm các thuộc tính cần thiết của một nhà điều hành bình đẳng (đối xứng, tính phản xạ và transitivity). Hôm nay tôi đã thiết kế một hệ thống loại từ đầu thì sẽ không có Equals( GetHashcodehoặc ToString) Objectcả.
Eric Lippert

33

Xử lý sự kiện thường có chữ ký MethodName(object sender, EventArgs e). Trong một số trường hợp, có thể xử lý sự kiện mà không cần quan tâm đến loại nào sender, hoặc thậm chí không sử dụng sender, nhưng trong các trường hợp khác senderphải chuyển sang loại cụ thể hơn để xử lý sự kiện.

Ví dụ: bạn có thể có hai TextBoxs và bạn muốn sử dụng một đại biểu duy nhất để xử lý một sự kiện từ mỗi người trong số họ. Sau đó, bạn sẽ cần truyền sendertới một TextBoxđể bạn có thể truy cập các thuộc tính cần thiết để xử lý sự kiện:

private void TextBox_TextChanged(object sender, EventArgs e)
{
   TextBox box = (TextBox)sender;
   box.BackColor = string.IsNullOrEmpty(box.Text) ? Color.Red : Color.White;
}

Vâng, trong ví dụ này, bạn có thể tạo lớp con của riêng bạn TextBoxđã thực hiện điều này trong nội bộ. Không phải lúc nào cũng thực tế hoặc có thể, mặc dù.


2
Cảm ơn bạn, đó là một ví dụ tốt. Sự TextChangedkiện này là một thành viên của Control- Tôi tự hỏi tại sao senderkhông phải là loại Controlthay vì loại object? Ngoài ra, tôi tự hỏi liệu (về mặt lý thuyết) khung có thể xác định lại sự kiện cho từng lớp con hay không (vì vậy, ví dụ như TextBoxcó thể có một phiên bản mới của sự kiện, sử dụng TextBoxlàm kiểu người gửi) ... có thể (hoặc có thể không) yêu cầu phát sóng ( đến TextBox) bên trong TextBoxtriển khai (để triển khai loại sự kiện mới), nhưng sẽ tránh yêu cầu hạ thấp trình xử lý sự kiện của mã ứng dụng.
ChrisW

3
Điều này được coi là chấp nhận được vì khó có thể khái quát hóa việc xử lý sự kiện nếu mỗi sự kiện có chữ ký phương thức riêng. Mặc dù tôi cho rằng đó là một giới hạn được chấp nhận thay vì một thứ được coi là thực tiễn tốt nhất bởi vì sự thay thế thực sự rắc rối hơn. Nếu có thể bắt đầu với một TextBox senderthay vì object senderkhông quá phức tạp mã, tôi chắc chắn nó sẽ được thực hiện.
Neil

6
@Neil Các hệ thống sự kiện được tạo riêng để cho phép chuyển tiếp các khối dữ liệu tùy ý đến các đối tượng tùy ý. Điều đó không thể thực hiện được nếu không có kiểm tra thời gian chạy về tính chính xác của kiểu.
Joker_vD

@Joker_vD Lý tưởng nhất là API tất nhiên sẽ hỗ trợ đánh dấu chữ ký gọi lại mạnh mẽ bằng cách sử dụng chống chỉ định chung. Việc .NET (áp đảo) không làm điều này chỉ đơn thuần là do thực tế là thuốc generic và hiệp phương sai đã được giới thiệu sau đó. - Nói cách khác, downcasting ở đây (và, nói chung, ở nơi khác) là bắt buộc do sự bất cập trong ngôn ngữ / API, không phải vì về cơ bản đó là một ý tưởng tốt.
Konrad Rudolph

@KonradRudolph Chắc chắn, nhưng bạn sẽ lưu trữ các sự kiện của các loại tùy ý trong hàng đợi sự kiện như thế nào? Bạn chỉ cần thoát khỏi hàng đợi và đi cho công văn trực tiếp?
Joker_vD

17

Bạn cần downcasting khi một cái gì đó cung cấp cho bạn một siêu kiểu và bạn cần xử lý nó khác nhau tùy thuộc vào loại phụ.

decimal value;
Donation d = user.getDonation();
if (d is CashDonation) {
     value = ((CashDonation)d).getValue();
}
if (d is ItemDonation) {
     value = itemValueEstimator.estimateValueOf(((ItemDonation)d).getItem());
}
System.out.println("Thank you for your generous donation of $" + value);

Không, điều này không có mùi. Trong một thế giới hoàn hảo Donationsẽ có một phương thức trừu tượng getValuevà mỗi kiểu con thực hiện sẽ có một cách thực hiện phù hợp.

Nhưng điều gì sẽ xảy ra nếu các lớp này đến từ thư viện mà bạn không thể hoặc không muốn thay đổi? Sau đó, không có lựa chọn khác.


Đây có vẻ là thời điểm tốt để sử dụng mô hình khách truy cập. Hoặc dynamictừ khóa đạt được kết quả tương tự.
2023861

3
@ user2023861 Không Tôi nghĩ rằng mẫu khách truy cập phụ thuộc vào sự hợp tác từ các lớp con Đóng góp - tức là Đóng góp cần khai báo một bản tóm tắt void accept(IDonationVisitor visitor), mà các lớp con của nó thực hiện bằng cách gọi các phương thức cụ thể (ví dụ quá tải) của IDonationVisitor.
ChrisW

@ChrisW, bạn nói đúng về điều đó. Không có sự hợp tác, bạn vẫn có thể làm điều đó bằng cách sử dụng dynamictừ khóa.
2023861

Trong trường hợp cụ thể này, bạn có thể thêm một phương thức ước tính vào Tài trợ.
dùng253751

@ user2023861: dynamicvẫn downcasting, chỉ chậm hơn.
Mehrdad

12

Để thêm vào câu trả lời của Eric Lippert vì tôi không thể bình luận ...

Bạn thường có thể tránh bị hạ thấp bằng cách cấu trúc lại API để sử dụng thuốc generic. Nhưng thuốc generic không được thêm vào ngôn ngữ cho đến phiên bản 2.0 . Vì vậy, ngay cả khi mã của riêng bạn không cần hỗ trợ các phiên bản ngôn ngữ cổ, bạn có thể thấy mình sử dụng các lớp kế thừa không được tham số hóa. Ví dụ, một số lớp có thể được định nghĩa để mang một trọng tải loại Object, mà bạn buộc phải chuyển sang loại mà bạn biết nó thực sự là.


Thật vậy, người ta có thể nói rằng các tướng trong Java hoặc C # về cơ bản chỉ là một trình bao bọc an toàn xung quanh việc lưu trữ các đối tượng siêu lớp và hạ thấp xuống lớp con chính xác (được kiểm tra bởi trình biên dịch) trước khi sử dụng lại các phần tử. (Không giống như C ++ mẫu, mà viết lại mã luôn luôn sử dụng các lớp con riêng của mình và do đó tránh được việc phải nhìn xuống - có thể tăng hiệu suất bộ nhớ, nếu thường xuyên tại các chi phí của thời gian biên dịch và kích thước thực thi.)
leftaroundabout

4

Có một sự đánh đổi giữa các ngôn ngữ gõ tĩnh và động. Gõ tĩnh cung cấp cho trình biên dịch rất nhiều thông tin để có thể đảm bảo khá mạnh mẽ về sự an toàn của (các phần của) chương trình. Tuy nhiên, điều này phải trả giá vì bạn không chỉ cần biết rằng chương trình của mình là đúng mà còn phải viết mã phù hợp để thuyết phục trình biên dịch rằng đây là trường hợp. Nói cách khác, đưa ra yêu sách dễ hơn là chứng minh chúng.

Có các cấu trúc "không an toàn" giúp bạn thực hiện các xác nhận chưa được chứng minh cho trình biên dịch. Ví dụ: các cuộc gọi vô điều kiện Nullable.Value, các chương trình truyền phát vô điều kiện, dynamiccác đối tượng, v.v. Chúng cho phép bạn xác nhận một yêu cầu ("Tôi khẳng định rằng đối tượng đó alà một String, nếu tôi sai, ném InvalidCastException") mà không cần phải chứng minh. Điều này có thể hữu ích trong trường hợp chứng minh nó khó hơn đáng kể.

Lạm dụng điều này là rủi ro, đó chính xác là lý do tại sao ký hiệu downcast rõ ràng tồn tại và là bắt buộc; đó là muối cú pháp nhằm thu hút sự chú ý vào một hoạt động không an toàn. Ngôn ngữ có thể đã được thực hiện với việc hạ thấp ngầm định (trong đó loại suy ra không rõ ràng), nhưng điều đó sẽ che giấu hoạt động không an toàn này, điều không mong muốn.


Tôi đoán tôi đang hỏi, sau đó, đối với một số ví dụ về nơi / khi cần thiết hoặc mong muốn (nghĩa là thực hành tốt) để thực hiện loại "khẳng định chưa được chứng minh" này.
ChrisW

1
Làm thế nào về việc đúc kết quả của một hoạt động phản ánh? stackoverflow.com/a/3255716/3141234
Alexander

3

Downcasting được coi là xấu vì một vài lý do. Chủ yếu tôi nghĩ bởi vì nó chống OOP.

OOP sẽ thực sự thích nó nếu bạn chưa bao giờ phải thất vọng vì '' raison-d'etre '' của nó là đa hình có nghĩa là bạn không phải đi

if(object is X)
{
    //do the thing we do with X's
}
else
{
    //of the thing we do with Y's
}

bạn chỉ cần làm

x.DoThing()

và mã tự động làm điều kỳ diệu.

Có một số lý do 'khó' không hạ thấp:

  • Trong C ++ thì chậm.
  • Bạn gặp lỗi thời gian chạy nếu bạn chọn sai loại.

Nhưng sự thay thế cho việc downcasting trong một số tình huống có thể khá xấu xí.

Ví dụ cổ điển là xử lý tin nhắn, trong đó bạn không muốn thêm chức năng xử lý vào đối tượng, nhưng giữ nó trong bộ xử lý tin nhắn. Sau đó tôi có một tấn MessageBaseClasstrong một mảng để xử lý, nhưng tôi cũng cần mỗi loại theo kiểu con để xử lý chính xác.

Bạn có thể sử dụng Double Dispatch để giải quyết vấn đề ( https://en.wikipedia.org/wiki/Double_dispatch ) ... Nhưng điều đó cũng có vấn đề của nó. Hoặc bạn chỉ có thể downcast cho một số mã đơn giản có nguy cơ của những lý do khó khăn.

Nhưng đây là trước khi Generics được phát minh. Bây giờ bạn có thể tránh downcasting bằng cách cung cấp một loại trong đó các chi tiết được chỉ định sau.

MessageProccesor<T> có thể thay đổi các tham số phương thức và trả về giá trị theo loại đã chỉ định nhưng vẫn cung cấp chức năng chung

Tất nhiên không ai bắt buộc bạn phải viết mã OOP và có rất nhiều tính năng ngôn ngữ được cung cấp nhưng không được chấp nhận, chẳng hạn như sự phản chiếu.


1
Tôi nghi ngờ nó chậm trong C ++, đặc biệt là nếu bạn static_castthay vì dynamic_cast.
ChrisW

"Xử lý tin nhắn" - Tôi nghĩ đó là ý nghĩa, ví dụ như truyền lParamđến một cái gì đó cụ thể. Tôi không chắc chắn đó là một ví dụ C # tốt, mặc dù.
ChrisW

3
@ChrisW - tốt, vâng, static_castluôn luôn nhanh ... nhưng không được đảm bảo sẽ không thất bại theo những cách không xác định nếu bạn mắc lỗi và loại không như bạn mong đợi.
Jules

@Jules: Điều đó hoàn toàn bên cạnh quan điểm.
Mehrdad

@Mehrdad, nó chính xác điểm. để làm c # cast tương đương trong c ++ thì chậm. và đây là lý do truyền thống chống lại việc đúc. static_cast thay thế không tương đương và được coi là lựa chọn tồi tệ hơn do hành vi không xác định
Ewan

0

Ngoài tất cả những gì đã nói trước đó, hãy tưởng tượng một thuộc tính Tag thuộc loại đối tượng. Nó cung cấp một cách để bạn lưu trữ một đối tượng bạn chọn trong một đối tượng khác để sử dụng sau này khi bạn cần. Bạn cần downcast tài sản này.

Nói chung, không sử dụng một cái gì đó cho đến ngày hôm nay không phải lúc nào cũng là một dấu hiệu của một cái gì đó vô dụng ;-)


Có xem ví dụ Sử dụng thuộc tính Tag trong .net . Trong một trong những câu trả lời ở đó, có người đã viết, tôi đã từng sử dụng nó để nhập hướng dẫn cho người dùng trong các ứng dụng Windows Forms. Khi kích hoạt sự kiện GotF Focus điều khiển, thuộc tính Label.Text được chỉ định giá trị của thuộc tính Thẻ điều khiển của tôi có chứa chuỗi lệnh. Tôi đoán một cách khác để 'mở rộng' Control(thay vì sử dụng thuộc object Tagtính) sẽ là tạo một Dictionary<Control, string>cái để lưu nhãn cho mỗi điều khiển.
ChrisW

1
Tôi thích câu trả lời này vì Taglà một thuộc tính công cộng của lớp khung .Net, cho thấy rằng bạn (ít nhiều) nên sử dụng nó.
ChrisW

@ChrisW: Không phải để nói kết luận là sai (hoặc phải), nhưng lưu ý rằng đó là lý do cẩu thả, bởi vì có rất nhiều chức năng NET công cộng mà lỗi thời (ví dụ System.Collections.ArrayList) hoặc nếu không bị phản đối (ví dụ IEnumerator.Reset).
Mehrdad

0

Việc sử dụng phổ biến nhất của downcasting trong công việc của tôi là do một số kẻ ngốc phá vỡ nguyên tắc Thay thế của Liskov.

Hãy tưởng tượng có một giao diện trong một số lib của bên thứ ba.

public interface Foo
{
     void SayHello();
     void SayGoodbye();
 }

Và 2 lớp thực hiện nó. BarQux. Barcư xử tốt

public class Bar
{
    void SayHello()
    {
        Console.WriteLine(“Bar says hello.”);
    }
    void SayGoodbye()
    {
         Console.WriteLine(“Bar says goodbye”);
    }
}

Nhưng Quxcư xử không tốt.

public class Qux
{
    void SayHello()
    {
        Console.WriteLine(“Qux says hello.”);
    }
    void SayGoodbye()
    {
        throw new NotImplementedException();
    }
}

Chà ... bây giờ tôi không có lựa chọn nào khác. Tôi phải gõ kiểm tra và (có thể) downcast để tránh việc chương trình của tôi bị dừng.


1
Không đúng với ví dụ cụ thể này. Bạn chỉ có thể bắt gặp NotIm HiệnedException khi gọi Sayoodbye.
Per von Zweigbergk

Đó có thể là một ví dụ đơn giản hóa quá mức , nhưng có thể hoạt động với một số API .Net cũ hơn một chút và bạn sẽ gặp phải tình huống này. Đôi khi cách dễ nhất để viết mã là an toàn đúc ala var qux = foo as Qux;.
RubberDuck

0

Một ví dụ thực tế khác là WPF's VisualTreeHelper. Nó sử dụng downcast để truyền DependencyObjecttới VisualVisual3D. Ví dụ: VisualTreeHelper.GetParent . Tình trạng downcast xảy ra trong VisualTreeUtils.AsVisualHelper :

private static bool AsVisualHelper(DependencyObject element, out Visual visual, out Visual3D visual3D)
{
    Visual elementAsVisual = element as Visual;

    if (elementAsVisual != null)
    {
        visual = elementAsVisual;
        visual3D = null;
        return true;
    }

    Visual3D elementAsVisual3D = element as Visual3D;

    if (elementAsVisual3D != null)
    {
        visual = null;
        visual3D = elementAsVisual3D;
        return true;
    }            

    visual = null;
    visual3D = null;
    return false;
}
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.