Operator casting langsung vs operator 'sebagai'?

710

Pertimbangkan kode berikut:

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
}

Apa perbedaan antara ketiga jenis casting (oke, yang ketiga bukan casting, tetapi Anda mendapatkan maksudnya). Yang mana yang lebih disukai?

nullDev
sumber
1
Bukan duplikat, tetapi ada juga beberapa diskusi kinerja dalam pertanyaan sebelumnya .
Tanpa izin
8
4 string s = Convert.ToString(o):; 5: string s = $"{o}"(atau setara dengan string.Formatformulir untuk C # sebelumnya)
Earth Engine

Jawaban:

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

Melemparkan InvalidCastException jika obukan string. Jika tidak, tetapkan ountuk s, bahkan jika oada null.

string s = o as string; // 2

Wakilnya nulluntuk sjika otidak stringatau jika oini null. Karena alasan ini, Anda tidak dapat menggunakannya dengan tipe nilai (operator tidak akan pernah bisa kembali nulldalam kasus itu). Jika tidak, tetapkan oke s.

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

Menyebabkan NullReferenceException jika oadalah null. Tetapkan apa pun yang o.ToString()dikembalikan s, apa pun jenisnya o.


Gunakan 1 untuk sebagian besar konversi - sederhana dan mudah. Saya cenderung hampir tidak pernah menggunakan 2 karena jika ada sesuatu yang bukan tipe yang tepat, saya biasanya mengharapkan pengecualian terjadi. Saya hanya melihat perlunya jenis fungsionalitas return-null ini dengan pustaka yang dirancang dengan buruk yang menggunakan kode kesalahan (mis. Return null = error, alih-alih menggunakan pengecualian).

3 bukan pemain dan hanya doa metode. Gunakan untuk ketika Anda membutuhkan representasi string dari objek non-string.

Sander
sumber
2
Anda dapat menetapkan 'null' untuk tipe-nilai ketika didefinisikan secara eksplisit, misalnya: int? saya; string s = "5"; i = s sebagai int; // i sekarang 5 s = null; i = s sebagai int; // aku sekarang nol
Anheledir
3
RE: Anheledir Sebenarnya saya akan menjadi null setelah panggilan pertama. Anda harus menggunakan fungsi konversi eksplisit untuk mendapatkan nilai string.
Guvante
45
RE: Sander Sebenarnya ada alasan lain yang sangat bagus untuk digunakan karena, ini menyederhanakan kode pemeriksaan Anda (Periksa untuk null daripada memeriksa untuk null dan jenis yang benar) Ini sangat membantu karena banyak waktu Anda lebih suka melempar satu pengecualian kustom. Tetapi benar bahwa buta panggilan itu buruk.
Guvante
5
# 2 berguna untuk hal-hal seperti metode Setara di mana Anda tidak tahu tipe inputnya. Umumnya, ya, 1 lebih disukai. Meskipun lebih disukai daripada yang jelas akan menggunakan sistem tipe untuk membatasi ke satu jenis ketika Anda hanya mengharapkan satu :)
Calum
6
# 2 juga berguna ketika Anda memiliki kode yang mungkin melakukan sesuatu yang spesifik untuk jenis khusus tetapi sebaliknya tidak akan melakukan apa-apa.
AnthonyWJones
349
  1. string s = (string)o;Gunakan ketika sesuatu pasti menjadi hal lain.
  2. string s = o as string;Gunakan ketika sesuatu mungkin menjadi hal lain.
  3. string s = o.ToString(); Gunakan ketika Anda tidak peduli apa itu tetapi Anda hanya ingin menggunakan representasi string yang tersedia.
Berdalih
sumber
1
Saya merasa jawaban ini terdengar bagus, tetapi mungkin tidak akurat.
j riv
1
Saya suka dua yang pertama, tetapi saya akan menambahkan "dan Anda yakin itu bukan nol" ke opsi ketiga.
Uxonith
2
Anda dapat menggunakan Elvis (?) hari ini untuk menghindari harus peduli tentang itu: obj?. ToString ()
Quibblesome
@ Quibblesome - jawaban yang bagus tetapi saya harus berhenti untuk memikirkan sanggahan Anda! itu benar-benar mengejutkan saya bahwa bahasa tersebut telah ada lebih dari 15 tahun. Rasanya seperti kemarin ketika kita semua "tegang" berusaha meyakinkan para senior untuk beralih ke C #.
Griswald_911
1
@ Quibblesome jawaban yang bagus: apakah Anda akan merasa terganggu jika saya menambahkan apa 1/2/3 sehingga tidak perlu gulir ke OP. Saya dengan SO akan memberi peringkat jawaban lama menurut suara!
whytheq
29

Itu benar-benar tergantung pada apakah Anda tahu apakah oitu string dan apa yang ingin Anda lakukan dengannya. Jika komentar Anda obenar-benar benar-benar string, saya lebih suka yang lurus (string)o- itu tidak mungkin gagal.

Keuntungan terbesar menggunakan straight cast adalah ketika gagal, Anda mendapatkan InvalidCastException , yang memberi tahu Anda apa yang salah.

Dengan asoperator, jika obukan string, sdiatur ke null, yang berguna jika Anda tidak yakin dan ingin menguji s:

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

Namun, jika Anda tidak melakukan tes itu, Anda akan menggunakannya snanti dan melempar NullReferenceException . Ini cenderung lebih umum dan jauh lebih sulit untuk dilacak begitu mereka terjadi di alam liar, karena hampir setiap baris referensi variabel dan mungkin melemparkan satu. Di sisi lain, jika Anda mencoba melakukan cast ke tipe nilai (primitif, atau struct seperti DateTime ), Anda harus menggunakan cast langsung - yang astidak akan berfungsi.

Dalam kasus khusus konversi ke string, setiap objek memiliki ToString, jadi metode ketiga Anda mungkin baik-baik saja jika otidak nol dan Anda berpikir ToStringmetode tersebut dapat melakukan apa yang Anda inginkan.

Blair Conrad
sumber
2
Satu catatan - Anda dapat menggunakan asdengan nullable jenis nilai. IE o as DateTimetidak akan bekerja, tetapi o as DateTime?akan ...
John Gibb
Kenapa tidak menggunakan if (s is string)saja?
BornToCode
1
@BornToCode, bagi saya, sebagian besar preferensi pribadi. Bergantung pada apa yang Anda lakukan, sering kali setelah ising, Anda harus melakukan cast lagi, jadi Anda memiliki is dan kemudian cast yang keras. Untuk beberapa alasan, ascek dan nol terasa lebih baik bagi saya.
Blair Conrad
9

Jika Anda sudah tahu tipe apa yang bisa dilemparkan, gunakan pemeran gaya-C:

var o = (string) iKnowThisIsAString; 

Perhatikan bahwa hanya dengan pemeran gaya C yang dapat Anda lakukan pemaksaan tipe eksplisit.

Jika Anda tidak tahu apakah itu tipe yang diinginkan dan Anda akan menggunakannya jika ya, gunakan sebagai kata kunci:

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

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

Perhatikan bahwa karena tidak akan memanggil operator konversi tipe apa pun. Ini hanya akan menjadi non-null jika objek tersebut tidak nol dan asli dari jenis yang ditentukan.

Gunakan ToString () untuk mendapatkan representasi string yang dapat dibaca manusia dari objek apa pun, bahkan jika itu tidak dapat dilemparkan ke string.

Mark Cidade
sumber
3
Itu sedikit mengerti tentang jenis operator konversi. Saya memiliki beberapa jenis konversi yang telah saya buat, harus diperhatikan saat itu.
AnthonyWJones
7

Kata kunci as bagus di asp.net saat Anda menggunakan metode FindControl.

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

Ini berarti Anda dapat beroperasi pada variabel yang diketik daripada harus membuangnya objectseperti yang Anda lakukan dengan pemain langsung:

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

Ini bukan hal yang besar, tetapi menghemat baris kode dan penugasan variabel, plus lebih mudah dibaca

Glenn Slaven
sumber
6

'as' didasarkan pada 'is', yang merupakan kata kunci yang memeriksa saat runtime jika objek tersebut kompatibel secara polimorfik (pada dasarnya jika para pemain dapat dibuat) dan mengembalikan nol jika pemeriksaan gagal.

Keduanya setara:

Menggunakan 'sebagai':

string s = o as string;

Menggunakan 'is':

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

Sebaliknya, para pemeran c-style juga dibuat pada saat runtime, tetapi melemparkan pengecualian jika pemeran tidak dapat dibuat.

Hanya untuk menambahkan fakta penting:

Kata kunci 'sebagai' hanya berfungsi dengan jenis referensi. Anda tidak dapat melakukan:

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

Dalam hal ini Anda harus menggunakan casting.

Sergio Acosta
sumber
Terima kasih telah menunjukkan kesalahan saya, Anda benar. Saya mengedit jawabannya. Ups, maaf.
Sergio Acosta
5

2 berguna untuk casting ke tipe turunan.

Misalkan a adalah Hewan:

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

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

akan mendapatkan sebuah makan dengan minimal gips.

Joel di Gö
sumber
2
@Chirs Moutray, itu tidak selalu mungkin, terutama jika itu adalah perpustakaan.
decaviatedcaviar
5

Menurut eksperimen yang dijalankan pada halaman ini: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(halaman ini terkadang memiliki beberapa kesalahan "pengarah ilegal" yang muncul, jadi segarkan jika ada)

Kesimpulannya adalah, operator "sebagai" biasanya lebih cepat daripada pemain. Kadang-kadang lebih cepat, kadang-kadang hanya lebih cepat.

Saya pribadi "sebagai" juga lebih mudah dibaca.

Jadi, karena lebih cepat dan "lebih aman" (tidak akan membuang pengecualian), dan mungkin lebih mudah dibaca, saya sarankan menggunakan "sebagai" sepanjang waktu.

Brady Moritz
sumber
4

"(string) o" akan menghasilkan InvalidCastException karena tidak ada pemeran langsung.

"o as string" akan menghasilkan menjadi referensi nol, bukan pengecualian yang dilemparkan.

"o.ToString ()" bukan gips dalam bentuk apa pun, ini adalah metode yang diimplementasikan oleh objek, dan dengan demikian dalam satu atau lain cara, oleh setiap kelas di .net yang "melakukan sesuatu" dengan contoh dari kelas yang dipanggil dan mengembalikan sebuah string.

Jangan lupa bahwa untuk mengkonversi ke string, ada juga Convert.ToString (someType instanceOfThatType) di mana someType adalah salah satu dari serangkaian jenis, pada dasarnya kerangka jenis dasar.

rampok
sumber
3

Semua jawaban yang diberikan baik, jika saya dapat menambahkan sesuatu: Untuk langsung menggunakan metode dan properti string (misalnya ToLower) Anda tidak dapat menulis:

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

Anda hanya bisa menulis:

((string)o).ToLower();

tetapi Anda bisa menulis sebagai gantinya:

(o as string).ToLower();

The aspilihan adalah lebih mudah dibaca (setidaknya untuk pendapat saya).

BornToCode
sumber
konstruksi (o as string) .ToLower () mengalahkan tujuan operator as. Ini akan melempar pengecualian referensi nol ketika o tidak dapat dilemparkan ke string.
james
@ James - Tapi siapa yang mengatakan bahwa tujuan utama operator as adalah untuk melemparkan pengecualian jika pemain gagal? Jika Anda tahu bahwa o adalah sebuah string dan hanya ingin menulis kode pembersih, Anda dapat menggunakan (o as string).ToLower()alih-alih beberapa tanda kurung yang membingungkan.
BornToCode
tujuan dari as sangat berlawanan - seharusnya tidak membuang pengecualian ketika para pemain gagal, itu harus mengembalikan nol. Katakanlah o Anda adalah string dengan nilai nol, lalu apa yang akan terjadi? Petunjuk - panggilan ToLower Anda akan gagal.
james
@ames - Anda benar, tetapi bagaimana dengan kasus di mana saya tahu pasti bahwa itu tidak akan nol dan saya hanya perlu melakukan casting untuk kompiler agar saya dapat mengakses metode objek itu?
BornToCode
1
Anda pasti bisa melakukan itu tetapi itu bukan praktik terbaik karena Anda tidak ingin bergantung pada pemanggil atau sistem eksternal untuk memastikan nilai Anda tidak nol. Jika Anda menggunakan C # 6 maka Anda bisa melakukannya (o sebagai string)? ToLower ().
james
3
string s = o as string; // 2

Lebih disukai, karena menghindari penalti kinerja casting ganda.

Chris S
sumber
Halo Chris, tautan yang ada dalam jawaban ini sekarang adalah 404 ... Saya tidak yakin apakah Anda memiliki pengganti yang ingin Anda masukkan di tempatnya?
Matt
3

Tampaknya keduanya secara konsep berbeda.

Casting langsung

Jenis tidak harus terkait erat. Muncul dalam semua jenis rasa.

  • Kustom casting implisit / eksplisit: Biasanya objek baru dibuat.
  • Jenis Nilai Tersirat: Salin tanpa kehilangan informasi.
  • Jenis Nilai Eksplisit: Salinan dan informasi mungkin hilang.
  • Hubungan IS-A: Ubah jenis referensi, jika tidak ada pengecualian.
  • Tipe yang sama: 'Casting redundant'.

Rasanya seperti objek akan dikonversi menjadi sesuatu yang lain.

SEBAGAI operator

Tipe memiliki hubungan langsung. Seperti dalam:

  • Jenis Referensi: IS-A hubungan Objek selalu sama, hanya perubahan referensi.
  • Jenis Nilai: Salin jenis tinju dan nullable.

Rasanya seperti Anda akan menangani objek dengan cara yang berbeda.

Sampel dan 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
    }
Lucas Teixeira
sumber
2

Saya ingin menarik perhatian pada spesifikasi operator sebagai berikut :

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

Perhatikan bahwa sebagai operator hanya melakukan konversi referensi, konversi nullable, dan konversi tinju. Operator as tidak dapat melakukan konversi lain, seperti konversi yang ditentukan pengguna, yang seharusnya dilakukan dengan menggunakan ekspresi pemeran.

Vadim S.
sumber
0

Ketika mencoba untuk mendapatkan representasi string dari apa pun (dari jenis apa pun) yang berpotensi menjadi nol, saya lebih suka baris kode di bawah ini. Ini kompak, itu memanggil ToString (), dan itu benar menangani nulls. Jika o adalah nol, s akan berisi String.Empty.

String s = String.Concat(o);
xtrem
sumber
0

Karena tidak ada yang menyebutkannya, yang terdekat dengan instanceOf ke Jawa dengan kata kunci adalah ini:

obj.GetType().IsInstanceOfType(otherObj)
Bennett Yeo
sumber
0

Gunakan pemeran langsung string s = (string) o;jika dalam konteks logis aplikasi Anda stringadalah satu-satunya jenis yang valid. Dengan pendekatan ini, Anda akan mendapatkan InvalidCastExceptiondan menerapkan prinsip Gagal-cepat . Logika Anda akan dilindungi dari meneruskan jenis yang tidak valid lebih lanjut atau mendapatkan NullReferenceException jika digunakan asoperator.

Jika logika mengharapkan beberapa tipe dilemparkan string s = o as string;dan periksa nullatau gunakan isoperator.

Fitur keren baru telah muncul di C # 7.0 untuk menyederhanakan pemain dan memeriksa apakah Pencocokan Pola :

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;
 }
Dmitry
sumber
0

Dua bentuk konversi tipe (casting) berikut ini didukung dalam C #:

|

(CV

• Konversi tipe statis v ke c dalam ekspresi yang diberikan

• Hanya mungkin jika tipe dinamis dari v adalah c, atau subtipe dari c

• Jika tidak, InvalidCastException dilemparkan

|

v sebagai C

• Varian non-fatal dari (c) v

• Jadi, konversi tipe statis v ke c dalam ekspresi yang diberikan

• Mengembalikan nol jika tipe dinamis v bukan c, atau subtipe dari c


sumber