Liệu nó có ý nghĩa khi sử dụng trên mạng như là một thay vì sử dụng ngay cả khi không có kiểm tra null? [đóng cửa]


351

Trong các blog phát triển, các ví dụ về mã trực tuyến và (gần đây) thậm chí là một cuốn sách, tôi cứ loạng choạng về mã như thế này:

var y = x as T;
y.SomeMethod();

hoặc, thậm chí tệ hơn:

(x as T).SomeMethod();

Điều đó không có ý nghĩa với tôi. Nếu bạn chắc chắn đó xlà loại T, bạn nên sử dụng truyền trực tiếp : (T)x. Nếu bạn không chắc chắn, bạn có thể sử dụng asnhưng cần kiểm tra nulltrước khi thực hiện một số thao tác. Tất cả những gì đoạn mã trên làm là biến một (hữu ích) InvalidCastExceptionthành một (vô dụng) NullReferenceException.

Tôi có phải là người duy nhất nghĩ rằng đây là một sự lạm dụng trắng trợn của astừ khóa không? Hoặc tôi đã bỏ lỡ một cái gì đó rõ ràng và mô hình trên thực sự có ý nghĩa?


56
Sẽ vui hơn khi thấy (hôn như S) .SteveIsSuchA (); Nhưng tôi đồng ý, đó là một sự lạm dụng.
SwDevMan81

5
Nó mát hơn nhiều so với viết ((T)x).SomeMethod(), phải không? ;) (đùa thôi, tất nhiên là bạn đúng!)
Lucero

8
@P Daddy Tôi không đồng ý, câu hỏi hoàn toàn tốt (mẫu mã này có thực sự có ý nghĩa không) và rất hữu ích. +1 cho câu hỏi và nhăn mặt với bất cứ ai bỏ phiếu để đóng.
MarkJ

9
Lucerno đã đúng, mô hình mã hóa này được tạo ra bằng cách cố gắng tránh dấu ngoặc đơn. Không thể chữa khỏi sau khi tiếp xúc với Lisp.
Hans Passant

13
Mã được tối ưu hóa : (f as T).SomeMethod();)
MSalters

Câu trả lời:


252

Sự hiểu biết của bạn là đúng. Nghe có vẻ như cố gắng tối ưu hóa vi mô đối với tôi. Bạn nên sử dụng một diễn viên bình thường khi bạn chắc chắn về loại. Bên cạnh việc tạo ra một ngoại lệ hợp lý hơn, nó cũng thất bại nhanh chóng. Nếu bạn sai sắp giả định của bạn về loại, chương trình của bạn sẽ thất bại ngay lập tức và bạn sẽ có thể thấy nguyên nhân của sự thất bại ngay lập tức thay vì chờ đợi cho một NullReferenceExceptionhoặc ArgumentNullExceptionhoặc thậm chí một đôi khi lỗi logic trong tương lai. Nói chung, một asbiểu thức không được theo sau bởi một nullkiểm tra ở đâu đó là mùi mã.

Mặt khác, nếu bạn không chắc chắn về dàn diễn viên và hy vọng nó sẽ thất bại, bạn nên sử dụng asthay vì một diễn viên bình thường được bọc bằng một try-catchkhối. Hơn nữa, việc sử dụng asđược khuyến nghị đối với kiểm tra loại tiếp theo là diễn viên. Thay vì:

if (x is SomeType)
   ((SomeType)x).SomeMethod();

trong đó tạo ra một isinsthướng dẫn cho istừ khóa và một castclasshướng dẫn cho các diễn viên (thực hiện hiệu quả các diễn viên hai lần), bạn nên sử dụng:

var v = x as SomeType;
if (v != null)
    v.SomeMethod();

Điều này chỉ tạo ra một isinsthướng dẫn. Phương thức trước đây có một lỗ hổng tiềm năng trong các ứng dụng đa luồng vì điều kiện chủng tộc có thể khiến biến số thay đổi loại sau khi iskiểm tra thành công và thất bại tại đường truyền. Phương pháp sau không dễ bị lỗi này.


Các giải pháp sau đây không được khuyến khích sử dụng trong mã sản xuất. Nếu bạn thực sự ghét một cấu trúc cơ bản như vậy trong C #, bạn có thể xem xét chuyển sang VB hoặc một số ngôn ngữ khác.

Trong trường hợp một người tuyệt vọng ghét cú pháp diễn viên, anh ấy / cô ấy có thể viết một phương thức mở rộng để bắt chước diễn viên:

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

và sử dụng cú pháp [?] gọn gàng:

obj.To<SomeType>().SomeMethod()

5
Tôi nghĩ rằng điều kiện cuộc đua là không liên quan. Nếu bạn đang gặp vấn đề này, thì mã của bạn không an toàn cho chuỗi và có nhiều cách đáng tin cậy hơn để giải quyết nó hơn là sử dụng từ khóa "as". +1 cho phần còn lại của câu trả lời.
RMorrisey

10
@RMorrisey: Tôi có ít nhất một ví dụ trong đầu: Giả sử bạn có một cacheđối tượng mà một luồng khác cố gắng vô hiệu hóa nó bằng cách đặt nó thành null. Trong các kịch bản không khóa, những loại công cụ này có thể phát sinh.
Mehrdad Afshari

10
is + cast là đủ để kích hoạt cảnh báo "Không cần thiết" từ FxCop: msdn.microsoft.com/en-us/l Library / ms182271.aspx Đó là đủ lý do để tránh cấu trúc.
David Schmitt

2
Bạn nên tránh thực hiện các phương pháp mở rộng trên Object. Việc sử dụng phương thức trên một loại giá trị sẽ khiến nó bị đóng hộp không cần thiết.
MgSam

2
@MgSam Rõ ràng, trường hợp sử dụng như vậy không có ý nghĩa đối với Tophương thức ở đây, vì nó chỉ chuyển đổi qua hệ thống phân cấp thừa kế, đối với các loại giá trị liên quan đến quyền anh. Tất nhiên, toàn bộ ý tưởng là lý thuyết nhiều hơn là nghiêm trọng.
Mehrdad Afshari

42

IMHO, aschỉ có ý nghĩa khi kết hợp với nullkiểm tra:

var y = x as T;
if (y != null)
    y.SomeMethod();

40

Sử dụng 'as' không áp dụng chuyển đổi do người dùng xác định trong khi diễn viên sẽ sử dụng chúng khi thích hợp. Đó có thể là một sự khác biệt quan trọng trong một số trường hợp.


5
Điều này rất quan trọng để nhớ. Eric Lippert đi qua đó tại đây: blog.msdn.com/ericlippert/archive/2009/10/08/NH
P Daddy

5
Bình luận tốt đẹp, P! Tuy nhiên, nếu mã của bạn phụ thuộc vào sự khác biệt này, tôi sẽ nói rằng có một phiên gỡ lỗi vào đêm khuya trong tương lai của bạn.
TrueWill

36

Tôi đã viết một chút về điều này ở đây:

http://bloss.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-b between-as-and-cast-operators.aspx

Tôi hiểu quan điểm của bạn. Và tôi đồng ý với lực đẩy của nó: rằng một toán tử diễn viên giao tiếp "Tôi chắc chắn rằng đối tượng này có thể được chuyển đổi thành loại đó và tôi sẵn sàng mạo hiểm ngoại lệ nếu tôi sai", trong khi một toán tử "như" giao tiếp "Tôi không chắc chắn rằng đối tượng này có thể được chuyển đổi thành loại đó; hãy cho tôi một giá trị null nếu tôi sai".

Tuy nhiên, có một sự khác biệt tinh tế. (x as T) .Whthing () truyền đạt "Tôi biết không chỉ x có thể được chuyển đổi thành T, mà hơn nữa, làm như vậy chỉ liên quan đến chuyển đổi tham chiếu hoặc bỏ hộp, và hơn nữa, x không phải là null". Điều đó không truyền đạt thông tin khác với ((T) x) .Whthing () và có lẽ đó là những gì tác giả của mã dự định.


1
Tôi không đồng ý với sự bảo vệ đầu cơ của bạn về tác giả của mã trong câu cuối cùng của bạn. ((T)x).Whatever() cũng thông báo rằng x[không có ý định] null và tôi rất nghi ngờ rằng một tác giả thường quan tâm đến việc chuyển đổi có Txảy ra chỉ với các chuyển đổi tham chiếu hoặc bỏ hộp hoặc nếu nó yêu cầu chuyển đổi do người dùng xác định hoặc thay đổi đại diện. Rốt cuộc, nếu tôi xác định public static explicit operator Foo(Bar b){}, thì rõ ràng ý định của tôi Barđược coi là tương thích Foo. Thật hiếm khi tôi muốn tránh chuyển đổi này.
P Daddy

4
Chà, có lẽ hầu hết các tác giả của mã sẽ không tạo ra sự khác biệt tinh tế đó. Cá nhân tôi có thể, nhưng nếu là tôi, tôi sẽ thêm một nhận xét cho hiệu ứng đó.
Eric Lippert

16

Tôi thường thấy các tài liệu tham khảo cho bài viết sai lệch này là bằng chứng cho thấy "như" nhanh hơn so với việc truyền.

Một trong những khía cạnh sai lệch rõ ràng hơn của bài viết này là đồ họa, không chỉ ra những gì đang được đo: Tôi nghi ngờ rằng nó đo lường các diễn viên thất bại (trong đó "như" rõ ràng là nhanh hơn nhiều vì không có ngoại lệ được ném).

Nếu bạn dành thời gian để thực hiện các phép đo, thì bạn sẽ thấy rằng việc truyền vai diễn, như bạn mong đợi, nhanh hơn "như" khi diễn viên thành công.

Tôi nghi ngờ đây có thể là một lý do cho việc "sùng bái hàng hóa" sử dụng từ khóa thay vì sử dụng từ khóa.


2
Cảm ơn các liên kết, đó là rất thú vị. Từ cách tôi hiểu bài báo, anh ta so sánh trường hợp không ngoại lệ. Tuy nhiên, bài viết được viết cho .net 1.1 và các ý kiến ​​chỉ ra rằng điều này đã thay đổi trong .net 2.0: Hiệu suất bây giờ gần như bằng nhau, với tiền tố được đúc thậm chí nhanh hơn một chút.
Heinzi

1
Bài báo này ngụ ý anh ta đang so sánh trường hợp không ngoại lệ, nhưng tôi đã thực hiện một số thử nghiệm từ lâu và không thể sao chép kết quả được yêu cầu của anh ta, ngay cả với .NET 1.x. Và vì bài viết không cung cấp mã được sử dụng để chạy điểm chuẩn, nên không thể nói những gì được so sánh.
Joe

"Sùng bái hàng hóa" - hoàn hảo. Kiểm tra "Cargo Cult Science Richard Feynman" để biết thông tin đầy đủ.
Bob Denny

11

Diễn viên trực tiếp cần một cặp dấu ngoặc đơn nhiều hơn astừ khóa. Vì vậy, ngay cả trong trường hợp bạn chắc chắn 100% loại đó là gì, nó sẽ làm giảm sự lộn xộn thị giác.

Đồng ý về điều ngoại lệ, mặc dù. Nhưng ít nhất là đối với tôi, hầu hết việc sử dụng asđun sôi để kiểm tra nullsau đó, điều mà tôi thấy đẹp hơn là bắt một ngoại lệ.


8

99% khi tôi sử dụng "as" là khi tôi không chắc loại đối tượng thực tế là gì

var x = obj as T;
if(x != null){
 //x was type T!
}

và tôi không muốn bắt ngoại lệ diễn viên rõ ràng cũng như không thực hiện hai lần, sử dụng "is":

//I don't like this
if(obj is T){
  var x = (T)obj; 
}

8
Bạn vừa mô tả trường hợp sử dụng thích hợp cho as. 1% còn lại là gì?
P Daddy

Trong một lỗi đánh máy? =) Tôi có nghĩa là 99% thời gian tôi sử dụng đoạn mã chính xác này , trong khi đôi khi tôi có thể sử dụng "as" trong một cuộc gọi phương thức hoặc một số nơi khác.
Max Galkin

D', và làm thế nào là ít hữu ích hơn câu trả lời phổ biến thứ hai ???
Max Galkin

2
+1 Tôi đồng ý gọi điều này cũng có giá trị như câu trả lời của Rubens Farias - mọi người sẽ hy vọng đến đây và đây sẽ là một ví dụ hữu ích
Ruben Bartelink

8

Chỉ vì mọi người thích vẻ ngoài của nó, nó rất dễ đọc.

Hãy đối mặt với nó: toán tử chuyển đổi / chuyển đổi trong các ngôn ngữ giống như C là khá khủng khiếp, dễ đọc. Tôi muốn nó tốt hơn nếu C # chấp nhận cú pháp Javascript của:

object o = 1;
int i = int(o);

Hoặc xác định một totoán tử, tương đương với as:

object o = 1;
int i = o to int;

Để bạn biết, cú pháp JavaScript mà bạn đề cập cũng được cho phép trong C ++.
P Daddy

@PDaddy: Tuy nhiên, đây không phải là cú pháp thay thế tương thích trực tiếp 100% và không nhằm mục đích đó (toán tử X so với hàm tạo chuyển đổi)
Ruben Bartelink

Tôi muốn nó sử dụng cú pháp C ++ của dynamic_cast<>()(và tương tự). Bạn đang làm một cái gì đó xấu xí, nó sẽ trông xấu xí.
Tom Hawtin - tackline

5

Mọi người thích asrất nhiều vì nó làm cho họ cảm thấy an toàn trước các ngoại lệ ... Giống như bảo đảm trên một hộp. Một anh chàng đặt một bảo đảm lạ mắt vào hộp 'vì anh ta muốn bạn cảm thấy tất cả ấm áp và ngon miệng bên trong. Bạn nghĩ rằng bạn đặt chiếc hộp nhỏ đó dưới gối vào ban đêm, Fairy Fairy có thể rơi xuống và để lại một phần tư, phải không Ted?

Quay lại chủ đề ... khi sử dụng diễn viên trực tiếp, có khả năng ngoại lệ diễn viên không hợp lệ. Vì vậy, mọi người áp dụng asnhư một giải pháp chăn cho tất cả các nhu cầu đúc của họ bởi vì as(tự nó) sẽ không bao giờ ném một ngoại lệ. Nhưng điều buồn cười về điều đó, là trong ví dụ bạn đưa ra, (x as T).SomeMethod();bạn đang giao dịch một ngoại lệ cast không hợp lệ cho một ngoại lệ tham chiếu null. Điều này làm khó hiểu vấn đề thực sự khi bạn nhìn thấy ngoại lệ.

Tôi thường không sử dụng asquá nhiều. Tôi thích isthử nghiệm hơn bởi vì đối với tôi, nó có vẻ dễ đọc hơn và có ý nghĩa hơn sau đó thử diễn viên và kiểm tra null.


2
"Tôi thích thử nghiệm là" - "là" theo sau là một diễn viên dĩ nhiên chậm hơn "như" sau đó là một thử nghiệm cho null (giống như "IDadata.ContainsKey", theo sau là hội thảo sử dụng bộ chỉ mục chậm hơn "IDadata.TryGetValue "). Nhưng nếu bạn thấy nó dễ đọc hơn, chắc chắn sự khác biệt hiếm khi đáng kể.
Joe

Tuyên bố quan trọng ở phần giữa là cách mọi người áp dụng asnhư một giải pháp chăn vì nó làm cho họ cảm thấy an toàn.
Bob

5

Đây phải là một trong những peeves hàng đầu của tôi .

D & E của Stroustrup và / hoặc một số bài đăng trên blog mà tôi không thể tìm thấy ngay bây giờ thảo luận về khái niệm của một tonhà điều hành sẽ giải quyết vấn đề được tạo bởi https://stackoverflow.com/users/73070/johannes-rossel (nghĩa là cùng một cú pháp như asnhưng với DirectCastngữ nghĩa ).

Lý do điều này không được thực hiện là vì diễn viên sẽ gây đau đớn và xấu xí nên bạn bị đẩy ra khỏi việc sử dụng nó.

Đáng tiếc là các lập trình viên 'thông minh' (thường là tác giả sách (Juval Lowy IIRC)) đã khắc phục điều này bằng cách lạm dụng asthời trang này (C ++ không cung cấp as, có lẽ vì lý do này).

Ngay cả VB cũng có sự nhất quán hơn trong việc có một cú pháp thống nhất buộc bạn phải chọn một TryCasthoặc DirectCastlàm cho tâm trí của bạn !


+1. Bạn có thể có nghĩa là DirectCast hành vi , không phải cú pháp .
Heinzi

@Heinzi: Ta cho +1. Điểm tốt. Quyết định trở thành một người thông minh và sử dụng semanticsthay thế: P
Ruben Bartelink

Cho rằng C # không có khả năng tương thích với C, C ++ hoặc Java, tôi thấy mình lúng túng trước một số điều mà nó mượn từ các ngôn ngữ đó. Nó vượt xa hơn "Tôi biết đây là X" và "Tôi biết đây không phải là X, nhưng có thể được biểu diễn dưới dạng một", đến "Tôi biết đây không phải là X và có thể không thực sự là một đại diện , nhưng dù sao cũng cho tôi điểm X. " Tôi có thể thấy sự hữu ích cho một double-to-intdiễn viên sẽ thất bại nếu doublekhông đại diện cho một giá trị chính xác có thể phù hợp với một Int32, nhưng có (int)-1.5năng suất -1 đơn giản là xấu.
supercat

@supercat Yep, nhưng thiết kế ngôn ngữ không dễ dàng như chúng ta đều biết - hãy nhìn vào tập hợp các sự đánh đổi liên quan đến C # nullables. Thuốc giải độc duy nhất được biết đến là việc đọc C # thường xuyên trong các phiên bản Depth khi chúng xuất hiện :) Rất may tôi quan tâm nhiều hơn đến việc hiểu các sắc thái của F # những ngày này và nó còn lành mạnh hơn rất nhiều về những vấn đề này.
Ruben Bartelink

@RubenBartelink: Tôi không rõ chính xác những vấn đề chính xác mà các loại không thể giải quyết được, nhưng tôi nghĩ trong hầu hết các trường hợp, sẽ tốt hơn nếu có MaybeValid<T>hai trường công khai IsValidValuemã nào có thể làm được khi nó phù hợp. Điều đó sẽ cho phép ví dụ MaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }. Điều đó không chỉ giúp tiết kiệm ít nhất hai thao tác sao chép so với Nullable<T>, mà còn có thể có giá trị với bất kỳ loại nào - khôngT chỉ các lớp.
supercat

2

Tôi tin rằng astừ khóa có thể được coi là một phiên bản trông thanh lịch hơn của dynamic_castC ++.


Tôi muốn nói rằng một dàn diễn viên thẳng trong C # giống dynamic_castvới C ++ hơn.
P Daddy

tôi nghĩ rằng dàn diễn viên thẳng trong C # tương đương với static_cast trong C ++.
Andrew Garrison

2
@Ruben Bartelink: Nó chỉ trả về null với con trỏ. Với các tài liệu tham khảo, mà bạn nên sử dụng khi có thể, nó sẽ ném std::bad_cast.
P Daddy

1
@Andrew Garrison: static_castthực hiện không kiểm tra loại thời gian chạy. Không có diễn viên tương tự như thế này trong C #.
P Daddy

Đáng buồn thay, tôi không biết bạn thậm chí có thể sử dụng các diễn viên trong các tài liệu tham khảo, vì tôi chỉ từng sử dụng chúng trên con trỏ, nhưng P Daddy hoàn toàn chính xác!
Andrew Garrison

1

Nó có thể phổ biến hơn không vì lý do kỹ thuật mà chỉ vì nó dễ đọc và trực quan hơn. (Không nói nó làm cho nó tốt hơn chỉ cố gắng trả lời câu hỏi)


1

Một lý do để sử dụng "as":

T t = obj as T;
 //some other thread changes obj to another type...
if (t != null) action(t); //still works

Thay vì (mã xấu):

if (obj is T)
{
     //bang, some other thread changes obj to another type...
     action((T)obj); //InvalidCastException
}

2
Nếu bạn gặp phải chủng tộc xấu xí này, bạn sẽ gặp vấn đề lớn hơn (nhưng đồng ý rằng đây là một mẫu hay để đi với những người khác nên +1
Ruben Bartelink

-1 vì điều này kéo dài một ngụy biện. Nếu các chủ đề khác có thể thay đổi loại obj, thì bạn vẫn có vấn đề. Khiếu nại "// vẫn hoạt động" rất không đúng, vì t sẽ được sử dụng làm con trỏ cho T, nhưng nó trỏ đến bộ nhớ không còn là giải pháp T. Cả hai sẽ không hoạt động khi luồng khác thay đổi loại obj trong khi hành động (t) đang diễn ra.
Stephen C. Steel

5
@Stephen C. Steel: Bạn có vẻ khá bối rối. Thay đổi kiểu objsẽ có nghĩa là thay đổi objchính biến để giữ tham chiếu đến đối tượng khác. Nó sẽ không thay đổi nội dung của bộ nhớ mà tại đó đối tượng được tham chiếu ban đầu bởi obj. Đối tượng ban đầu này sẽ không thay đổi và tbiến vẫn sẽ giữ một tham chiếu đến nó.
P Daddy


1
@P Daddy - Tôi nghĩ bạn đã đúng, và tôi đã sai: nếu obj bật lại từ một đối tượng T sang một đối tượng T2, thì t vẫn sẽ chỉ vào đối tượng T cũ. Vì t vẫn tham chiếu đối tượng cũ, nó không thể là rác được thu thập, vì vậy đối tượng T cũ sẽ vẫn còn hiệu lực. Các mạch phát hiện tình trạng chủng tộc của tôi đã được đào tạo về C ++, trong đó mã tương tự sử dụng Dynamic_cast sẽ là một vấn đề tiềm năng.
Stephen C. Steel
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.