Đúc trực tiếp vs 'như' toán tử?


709

Hãy xem xét các mã sau đây:

void Handler(object o, EventArgs e)
{
   // I swear o is a string
   string s = (string)o; // 1
   //-OR-
   string s = o as string; // 2
   // -OR-
   string s = o.ToString(); // 3
}

Sự khác biệt giữa ba loại đúc (không sao, loại thứ ba không phải là đúc, nhưng bạn có ý định). Cái nào nên được ưu tiên?


1
Không hoàn toàn trùng lặp, nhưng cũng có một số thảo luận về hiệu suất trong một câu hỏi trước đó .
Unsliced

8
Thứ 4 : string s = Convert.ToString(o); Thứ 5: string s = $"{o}"(hoặc tương đương là string.Formatmẫu cho C # trước đó)
Earth Engine

Câu trả lời:


834
string s = (string)o; // 1

Ném UnlimitedCastException nếu okhông phải là a string. Nếu không, gán ocho s, ngay cả khi onull.

string s = o as string; // 2

Chuyển nhượng nulltới snếu okhông phải là một stringhoặc nếu onull. Vì lý do này, bạn không thể sử dụng nó với các loại giá trị (toán tử không bao giờ có thể trả về nulltrong trường hợp đó). Nếu không, gán ocho s.

string s = o.ToString(); // 3

Gây ra một NullReferenceException nếu onull. Chỉ định bất cứ điều gì o.ToString()trả về s, bất kể olà loại nào .


Sử dụng 1 cho hầu hết các chuyển đổi - đơn giản và dễ hiểu. Tôi có xu hướng gần như không bao giờ sử dụng 2 vì nếu một cái gì đó không đúng loại, tôi thường mong đợi một ngoại lệ xảy ra. Tôi chỉ thấy nhu cầu về loại chức năng trả về null này với các thư viện được thiết kế tồi sử dụng mã lỗi (ví dụ: return null = error, thay vì sử dụng ngoại lệ).

3 không phải là một diễn viên và chỉ là một lời gọi phương thức. Sử dụng nó khi bạn cần biểu diễn chuỗi của một đối tượng không phải chuỗi.


2
Bạn có thể gán 'null' cho các loại giá trị khi được xác định rõ ràng, ví dụ: int? Tôi; chuỗi s = "5"; i = s là int; // tôi bây giờ là 5 s = null; i = s là int; // tôi bây giờ là null
Anheledir

3
RE: Anheledir Thật ra tôi sẽ bị null sau cuộc gọi đầu tiên. Bạn phải sử dụng hàm chuyển đổi rõ ràng để lấy giá trị của chuỗi.
Guvante

45
RE: Sander Trên thực tế có một lý do rất tốt khác để sử dụng, đó là đơn giản hóa mã kiểm tra của bạn (Kiểm tra null thay vì kiểm tra null và loại đúng) Điều này rất hữu ích vì nhiều khi bạn muốn ném một ngoại lệ tùy chỉnh. Nhưng rất đúng là mù như các cuộc gọi là xấu.
Guvante

5
# 2 tiện dụng cho những thứ như phương thức Equals nơi bạn không biết loại đầu vào. Nói chung, vâng, 1 sẽ được ưu tiên. Mặc dù được ưu tiên hơn nhưng rõ ràng là sẽ sử dụng hệ thống loại để hạn chế thành một loại khi bạn chỉ mong đợi một loại :)
Calum

6
# 2 cũng hữu ích khi bạn có mã có thể làm một cái gì đó cụ thể cho một loại chuyên biệt nhưng nếu không thì sẽ không làm gì cả.
AnthonyWJones

349
  1. string s = (string)o;Sử dụng khi một cái gì đó chắc chắn nên là thứ khác.
  2. string s = o as string;Sử dụng khi một cái gì đó có thể là điều khác.
  3. string s = o.ToString(); Sử dụng khi bạn không quan tâm nó là gì nhưng bạn chỉ muốn sử dụng biểu diễn chuỗi có sẵn.

1
Tôi có cảm giác câu trả lời này nghe có vẻ hay, nhưng nó có thể không chính xác.
j đối thủ

1
Tôi thích hai cái đầu tiên, nhưng tôi sẽ thêm "và bạn chắc chắn nó không rỗng" vào tùy chọn thứ ba.
Uxonith

2
bạn có thể sử dụng Elvis (?.) những ngày này để tránh phải quan tâm đến điều đó: obj? .ToString ()
Quibbledome

@Quibbledome - câu trả lời tuyệt vời nhưng tôi đã phải dừng lại để suy nghĩ về câu trả lời của bạn! nó thực sự thổi vào tâm trí tôi rằng ngôn ngữ đã tồn tại hơn 15 năm. Cảm giác như ngày hôm qua khi tất cả chúng ta đều "cáu kỉnh" khi cố gắng thuyết phục các nhà phát triển cấp cao chuyển sang C #.
Griswald_911

1
@Quibbledome câu trả lời hay: bạn sẽ thấy khó chịu nếu tôi thêm vào 1/2/3 là gì để không cần phải cuộn lên OP. Tôi với SO sẽ xếp hạng các câu trả lời cũ theo phiếu bầu!
whytheq

29

Nó thực sự phụ thuộc vào việc bạn có biết nếu olà một chuỗi và những gì bạn muốn làm với nó. Nếu nhận xét của bạn có nghĩa là othực sự là một chuỗi, tôi thích (string)odiễn viên thẳng - không chắc là thất bại.

Ưu điểm lớn nhất của việc sử dụng dàn diễn viên thẳng là khi nó thất bại, bạn nhận được một UnlimitedCastException , điều này cho bạn biết khá nhiều điều đã xảy ra.

Với astoán tử, nếu okhông phải là một chuỗi, sđược đặt thành thuận nulltiện nếu bạn không chắc chắn và muốn kiểm tra s:

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

Tuy nhiên, nếu bạn không thực hiện kiểm tra đó, bạn sẽ sử dụng ssau và ném NullReferenceException . Những xu hướng phổ biến hơn và nhiều khó khăn hơn để theo dõi xuống một khi họ xảy ra trong môi trường tự nhiên, như hầu hết các dòng dereferences một biến và có thể ném một. Mặt khác, nếu bạn đang cố gắng chuyển sang loại giá trị (bất kỳ cấu trúc nguyên thủy hoặc cấu trúc nào như DateTime ), bạn phải sử dụng truyền thẳng - công việc assẽ không hoạt động.

Trong trường hợp đặc biệt chuyển đổi thành một chuỗi, mọi đối tượng đều có một ToString, vì vậy phương thức thứ ba của bạn có thể ổn nếu okhông null và bạn nghĩ ToStringphương thức đó có thể làm những gì bạn muốn.


2
Một lưu ý - bạn có thể sử dụng asvới các loại giá trị nullable . IE o as DateTimesẽ không hoạt động, nhưng o as DateTime?sẽ ...
John Gibb

Tại sao không sử dụng if (s is string)thay thế?
Sinh ra ToCode

1
@BornToCode, đối với tôi, phần lớn là sở thích cá nhân. Tùy thuộc vào những gì bạn đang làm, thường sau khi ising, dù sao đi nữa, bạn sẽ phải chọn lại, vì vậy bạn có và sau đó là một diễn viên khó. Đối với một số lý do, askiểm tra và null cảm thấy tốt hơn đối với tôi.
Blair Conrad

9

Nếu bạn đã biết loại nào có thể sử dụng, hãy sử dụng kiểu đúc C:

var o = (string) iKnowThisIsAString; 

Lưu ý rằng chỉ với dàn diễn viên kiểu C, bạn mới có thể thực hiện cưỡng chế kiểu rõ ràng.

Nếu bạn không biết liệu đó có phải là loại mong muốn hay không và bạn sẽ sử dụng nó nếu có, hãy sử dụng làm từ khóa:

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

Lưu ý rằng như sẽ không gọi cho bất kỳ nhà khai thác loại chuyển đổi. Nó sẽ chỉ là null nếu đối tượng không phải là null và nguyên bản của loại được chỉ định.

Sử dụng ToString () để có được biểu diễn chuỗi có thể đọc được của con người đối với bất kỳ đối tượng nào, ngay cả khi nó không thể truyền thành chuỗi.


3
Đó là một vấn đề nhỏ thú vị về các toán tử chuyển đổi loại. Tôi có một vài loại mà tôi đã tạo chuyển đổi, phải coi chừng đó.
AnthonyWJones

7

Từ khóa as rất tốt trong asp.net khi bạn sử dụng phương thức FindControl.

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

Điều này có nghĩa là bạn có thể hoạt động trên biến được gõ thay vì sau đó phải chuyển nó từ objectgiống như bạn thực hiện với một vai trực tiếp:

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

Nó không phải là một điều lớn, nhưng nó tiết kiệm các dòng mã và gán biến, cộng với nó dễ đọc hơn


6

'as' dựa trên 'is', đây là một từ khóa kiểm tra trong thời gian chạy nếu đối tượng tương thích đa hình (về cơ bản nếu có thể tạo một cast) và trả về null nếu kiểm tra thất bại.

Hai cái này tương đương nhau:

Sử dụng 'như':

string s = o as string;

Sử dụng 'là':

if(o is string) 
    s = o;
else
    s = null;

Ngược lại, dàn diễn viên kiểu c cũng được tạo ra trong thời gian chạy, nhưng sẽ ném một ngoại lệ nếu diễn viên không thể thực hiện được.

Chỉ cần thêm một thực tế quan trọng:

Từ khóa 'as' chỉ hoạt động với các loại tham chiếu. Bạn không thể làm được:

// I swear i is an int
int number = i as int;

Trong những trường hợp bạn phải sử dụng đúc.


Cảm ơn đã chỉ ra lỗi lầm của tôi, bạn đã đúng. Tôi chỉnh sửa câu trả lời. ối xin lỗi.
Sergio Acosta

5

2 là hữu ích để đúc thành một loại dẫn xuất.

Giả sử a là động vật:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

sẽ nhận được một ăn với tối thiểu là phôi.


2
@Chirs Moutray, điều đó không phải lúc nào cũng có thể, đặc biệt nếu đó là một thư viện.
giảm tốc

5

Theo các thử nghiệm chạy trên trang này: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(đôi khi trang này có một số lỗi "người giới thiệu bất hợp pháp" xuất hiện, vì vậy chỉ cần làm mới nếu có)

Kết luận là, toán tử "as" thường nhanh hơn so với cast. Đôi khi nhanh hơn nhiều lần, đôi khi chỉ nhanh hơn.

Tôi thường xuyên điều "như" cũng dễ đọc hơn.

Vì vậy, vì nó nhanh hơn và "an toàn hơn" (không ném ngoại lệ), và có thể dễ đọc hơn, tôi khuyên bạn nên sử dụng "như" mọi lúc.


4

"(Chuỗi) o" sẽ dẫn đến một UnlimitedCastException vì không có diễn viên trực tiếp.

"o as string" sẽ dẫn đến việc nó là một tham chiếu null, thay vì một ngoại lệ được đưa ra.

"Ô lớp được gọi và trả về một chuỗi.

Đừng quên rằng để chuyển đổi thành chuỗi, cũng có Convert.ToString (someType instanceOfThatType) trong đó someType là một trong các loại, về cơ bản là các loại cơ sở của khung.


3

Tất cả các câu trả lời đều tốt, nếu tôi có thể thêm một cái gì đó: Để trực tiếp sử dụng các phương thức và thuộc tính của chuỗi (ví dụ ToLower), bạn không thể viết:

(string)o.ToLower(); // won't compile

bạn chỉ có thể viết:

((string)o).ToLower();

nhưng bạn có thể viết thay thế:

(o as string).ToLower();

Các aslựa chọn là dễ đọc hơn (ít nhất là với ý kiến của tôi).


cấu trúc (o as chuỗi) .ToLower () đánh bại mục đích của toán tử as. Điều này sẽ đưa ra một ngoại lệ tham chiếu null khi o không thể chuyển thành chuỗi.
James

@james - Nhưng ai nói rằng mục đích duy nhất của nhà điều hành là ném ngoại lệ nếu diễn viên thất bại? Nếu bạn biết rằng o là một chuỗi và chỉ muốn viết mã sạch hơn, bạn có thể sử dụng (o as string).ToLower()thay vì nhiều dấu ngoặc khó hiểu.
Sinh ra ToCode

mục đích của hoàn toàn ngược lại - không nên ném ngoại lệ khi diễn viên thất bại, nó sẽ trả về null. Giả sử o của bạn là một chuỗi có giá trị null, điều gì sẽ xảy ra sau đó? Gợi ý - cuộc gọi ToLower của bạn sẽ thất bại.
james

@james - Bạn nói đúng, nhưng những trường hợp mà tôi biết chắc chắn rằng nó sẽ không có giá trị và tôi chỉ cần thực hiện việc truyền cho trình biên dịch để cho phép tôi truy cập các phương thức của đối tượng đó?
Sinh ra ToCode

1
bạn chắc chắn có thể làm điều đó nhưng đó không phải là cách thực hành tốt nhất vì bạn không muốn dựa vào người gọi hoặc hệ thống bên ngoài để đảm bảo giá trị của bạn không có giá trị. Nếu bạn đang sử dụng C # 6 thì bạn có thể làm (o dưới dạng chuỗi)?. ToLower ().
james

3
string s = o as string; // 2

Được ưa thích hơn, vì nó tránh được hình phạt hiệu suất của việc đúc đôi.


Xin chào Chris, liên kết trong câu trả lời này hiện là 404 ... Tôi không chắc bạn đã có người thay thế mà bạn muốn đặt ở vị trí đó chưa?
Matt

3

Có vẻ như hai người họ khác nhau về mặt khái niệm.

Đúc trực tiếp

Các loại không phải liên quan chặt chẽ. Nó có trong tất cả các loại hương vị.

  • Tùy chỉnh ẩn / đúc rõ ràng: Thông thường một đối tượng mới được tạo.
  • Loại giá trị tiềm ẩn: Sao chép mà không mất thông tin.
  • Loại giá trị rõ ràng: Sao chép và thông tin có thể bị mất.
  • Mối quan hệ IS-A: Thay đổi loại tham chiếu, nếu không sẽ ném ngoại lệ.
  • Cùng loại: 'Đúc là dư thừa'.

Cảm giác như vật thể sẽ được chuyển đổi thành thứ khác.

Toán tử AS

Các loại có mối quan hệ trực tiếp. Như trong:

  • Các kiểu tham chiếu: Mối quan hệ IS-A Các đối tượng luôn giống nhau, chỉ là các thay đổi tham chiếu.
  • Các loại giá trị: Sao chép các loại quyền anh và nullable.

Cảm giác như bạn sẽ xử lý đối tượng theo một cách khác.

Mẫu và IL

    class TypeA
    {
        public int value;
    }

    class TypeB
    {
        public int number;

        public static explicit operator TypeB(TypeA v)
        {
            return new TypeB() { number = v.value };
        }
    }

    class TypeC : TypeB { }
    interface IFoo { }
    class TypeD : TypeA, IFoo { }

    void Run()
    {
        TypeA customTypeA = new TypeD() { value = 10 };
        long longValue = long.MaxValue;
        int intValue = int.MaxValue;

        // Casting 
        TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL:  call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
        IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass  ConsoleApp1.Program/IFoo

        int loseValue = (int)longValue; // explicit -- IL: conv.i4
        long dontLose = intValue; // implict -- IL: conv.i8

        // AS 
        int? wraps = intValue as int?; // nullable wrapper -- IL:  call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
        object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
        TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
        IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo

        //TypeC d = customTypeA as TypeC; // wouldn't compile
    }

2

Tôi muốn thu hút sự chú ý đến chi tiết cụ thể sau đây của như điều hành:

https://docs.microsoft.com/en-us/dotnet/csharp/lingu-reference/keywords/as

Lưu ý rằng toán tử dưới dạng chỉ thực hiện chuyển đổi tham chiếu, chuyển đổi nullable và chuyển đổi quyền anh. Toán tử as không thể thực hiện các chuyển đổi khác, chẳng hạn như chuyển đổi do người dùng xác định, thay vào đó nên được thực hiện bằng cách sử dụng biểu thức truyền.


0

Khi cố gắng để có được đại diện chuỗi của bất kỳ thứ gì (thuộc bất kỳ loại nào) có khả năng là null, tôi thích dòng mã dưới đây. Nó nhỏ gọn, nó gọi ToString () và nó xử lý chính xác null. Nếu o là null, s sẽ chứa String.Empty.

String s = String.Concat(o);

0

Vì không ai đề cập đến nó, gần nhất với instanceOf với Java theo từ khóa là:

obj.GetType().IsInstanceOfType(otherObj)

0

Sử dụng truyền trực tiếp string s = (string) o;nếu trong ngữ cảnh logic của ứng dụng của bạn stringlà loại hợp lệ duy nhất. Với phương pháp này, bạn sẽ có được InvalidCastExceptionvà thực hiện nguyên tắc Fail-fast . Logic của bạn sẽ được bảo vệ khỏi việc chuyển loại không hợp lệ hơn nữa hoặc nhận NullReferenceException nếu sử dụng astoán tử.

Nếu logic mong đợi một số loại khác nhau đúc string s = o as string;và kiểm tra nó nullhoặc sử dụng istoán tử.

Tính năng thú vị mới đã xuất hiện trong C # 7.0 để đơn giản hóa việc chọn và kiểm tra xem có khớp với Mẫu không :

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }

0

Hai hình thức chuyển đổi loại (đúc) sau đây được hỗ trợ trong C #:

|

(C) v

• Chuyển đổi kiểu tĩnh của v thành c trong biểu thức đã cho

• Chỉ có thể nếu loại động của v là c hoặc một kiểu con của c

• Nếu không, một UnlimitedCastException sẽ bị ném

|

v là C

• Biến thể không gây tử vong của (c) v

• Do đó, chuyển đổi kiểu tĩnh của v thành c trong biểu thức đã cho

• Trả về null nếu loại động của v không phải là c hoặc một kiểu con của 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.