Doğrudan oyuncuya karşı 'as' operatörü mü?


711

Aşağıdaki kodu göz önünde bulundurun:

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
}

Üç döküm türü arasındaki fark nedir (tamam, 3. döküm bir döküm değildir, ancak niyeti alırsınız). Hangisi tercih edilmeli?


1
Tam bir kopya değil, aynı zamanda önceki bir soruda bazı performans tartışmaları da var .
08:23

8
4 string s = Convert.ToString(o):; 5: string s = $"{o}"(veya eşdeğerde string.Formatdaha önceki C # için form)
Earth Engine

Yanıtlar:


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

Atar ınvalidcastexception eğer obir değil string. Aksi takdirde, atar okadar s, bile oolduğunu null.

string s = o as string; // 2

Atar nulliçin seğer obir değil stringya, eğer oolduğunu null. Bu nedenle, değer türleriyle kullanamazsınız (operatör nullbu durumda asla geri dönemez ). Aksi takdirde, atar oetmek s.

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

Eğer bir NullReferenceException özelliğine neden oolur null. Hangi tür olursa olsun, ne o.ToString()döndürürse onu atar .so


Çoğu dönüşüm için 1 kullanın - basit ve anlaşılır. Neredeyse hiç 2 kullanmıyorum çünkü bir şey doğru tip değilse, genellikle bir istisna oluşmasını beklerim. Ben sadece hata kodları (örn. Dönüş null = hata, istisnalar kullanmak yerine) kullanan kötü tasarlanmış kütüphaneleri ile işlevsellik bu dönüş null türü ihtiyacını gördük.

3 bir kadro değildir ve sadece bir yöntem çağrıştırmasıdır. Dize olmayan bir nesnenin dize olarak temsil edilmesi gerektiğinde bunu kullanın.


2
Açık olarak tanımlandığında değer türlerine 'null' atayabilirsiniz, örneğin: int? ben; dize s = "5"; int olarak i = s; // i şimdi 5 s = null; int olarak i = s; // şimdi boş
Anheledir

3
RE: Anheledir Aslında ilk görüşmeden sonra boş kalırdım. Bir dizenin değerini almak için açık bir dönüştürme işlevi kullanmanız gerekir.
Guvante

45
RE: Sander Aslında kullanmak için başka bir çok iyi neden var, kontrol kodunuzu basitleştirir (null olup olmadığını kontrol edin, sonra null ve doğru türü kontrol edin) Bu, çoğu zaman özel bir istisna atmayı tercih ettiğiniz için yararlıdır. Ancak körlerin aramaların kötü olduğu çok doğrudur.
Guvante

5
# 2, giriş tipini bilmediğiniz Eşit yöntemler gibi şeyler için kullanışlıdır.Genel olarak, evet, 1 tercih edilir. Her ne kadar tercih edilirse de, sadece birini beklediğinizde bir
türle

6
# 2, özel bir türe özgü bir şey yapabilen, ancak başka bir şey yapmayacak bir kodunuz olduğunda da yararlıdır.
AnthonyWJones

350
  1. string s = (string)o;Bir şeyin kesinlikle başka bir şey olması gerektiğinde kullanın .
  2. string s = o as string;Bir şey olduğunda kullanın olabilir başka şey.
  3. string s = o.ToString(); Ne olduğunu umursamadığınızda kullanın, ancak sadece kullanılabilir dize gösterimini kullanmak istiyorsunuz.

1
Bu cevabın kulağa hoş geldiğini anlıyorum, ama doğru olmayabilir.
j riv

1
İlk ikisini beğendim, ancak üçüncü seçeneğe "ve bunun boş olmadığından eminsiniz" i eklerdim.
Uxonith

2
Bu günlerde Elvis (?.) ile ilgilenmekten kaçınmak için kullanabilirsiniz: obj? .ToString ()
Quibblesome

@Quibblesome - harika cevap ama çürütmeyi düşünmek zorunda kaldım! kelimenin tam anlamıyla dilin 15 yıldan fazla sürdüğü aklıma geliyor. Dün hepimiz "sinirli" C # geçiş yapmak için üst düzey devs ikna etmeye çalışıyor gibi hissediyor.
Griswald_911

1
@Quibblesome güzel cevap: Ben 1/2/3 ne eklemek eklerseniz rahatsız olacak, böylece OP kadar kaydırma gerekli değildir. Ben SO ile oylara göre eski cevapları rütbe!
whytheq

29

Gerçekten obir dize olup olmadığını ve onunla ne yapmak istediğinizi bilmenize bağlıdır . oYorumunuz gerçekten bir dize olduğu anlamına gelirse , düz (string)ooyuncu kadrosunu tercih ederim - başarısız olma olasılığı düşüktür.

Düz döküm kullanmanın en büyük avantajı, başarısız olduğunda, neyin yanlış gittiğini size söyleyen bir InvalidCastException almanızdır .

İle aseğer operatör, obir dize değil, sayarlandığında nullEmin konum ve test etmek istiyorsanız kullanışlı olduğu s:

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

Ancak, bu sınamayı gerçekleştirmezseniz, sdaha sonra kullanırsınız ve bir NullReferenceException özel durumu oluşturulur . Bunlar daha yaygın olma eğilimindedir ve vahşi doğada ortaya çıktıklarında izlenmesi çok daha zordur, çünkü neredeyse her çizgi bir değişkeni dereferences ve bir tane atabilir. Öte yandan, bir değer türüne (herhangi bir ilkel veya DateTime gibi yapılar ) yayınlamaya çalışıyorsanız , düz döküm kullanmanız gerekir - asişe yaramaz.

Bir dizeye dönüştürme özel durumunda, her nesnenin bir vardır ToString, bu nedenle üçüncü yönteminiz oboş değilse doğru ToStringolabilir ve yöntemin istediğinizi yapabileceğini düşünüyorsunuz .


2
Bir not - kullanabilirsiniz asile null değer türleri. IE o as DateTimeçalışmaz, ama o as DateTime?...
John Gibb

Neden kullanmıyorsunuz if (s is string)?
BornToCode

1
@BornToCode, benim için büyük ölçüde kişisel tercih. Ne yaptığınıza bağlı olarak, genellikle ising sonra , yine de tekrar döküm yapmak zorunda kalacaksınız, bu yüzden is ve sonra sert bir oyuncuya sahip olursunuz. Nedense asnull çek bana daha iyi geldi.
Blair Conrad

9

Ne tür bir yayın yapabileceğini biliyorsanız, C tarzı bir yayın kullanın:

var o = (string) iKnowThisIsAString; 

Yalnızca C stili bir dökümde açık tür baskı uygulayabileceğinizi unutmayın.

İstediğiniz türde olup olmadığını bilmiyorsanız ve varsa kullanacaksanız , anahtar kelime olarak kullanın :

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

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

Gibi herhangi bir tür dönüştürme işleçleri çağırmaz unutmayın . Yalnızca nesne null değilse ve belirtilen türde yerel değilse null olmaz.

ToString () yöntemini kullanarak, herhangi bir nesnenin dizeye yayınlanamasa bile insan tarafından okunabilir bir dize temsilini elde edin.


3
Bu tür dönüştürme operatörleri ile ilgili ilginç küçük bir yakaladım. Dönüşümler için oluşturduğum birkaç tür var, o zaman buna dikkat etmeliyim.
AnthonyWJones

7

FindControl yöntemini kullandığınızda as anahtar sözcüğü asp.net iyi.

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

Bu, doğrudan değişkenle objectyaptığınız gibi döküm yapmak yerine yazılan değişken üzerinde çalışabileceğiniz anlamına gelir :

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

Çok büyük bir şey değil, ancak kod satırlarını ve değişken atamayı kaydediyor, ayrıca daha okunabilir


6

'as' is based on 'is', nesnenin polimorfik olarak uyumlu olup olmadığını (temel olarak bir döküm yapılabiliyorsa) kontrol eden ve kontrol başarısız olursa null değerini döndüren bir anahtar kelime olan 'is' temelidir.

Bu ikisi eşdeğerdir:

'As' kullanarak:

string s = o as string;

'İs' kullanmak:

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

Aksine, c tarzı döküm de çalışma zamanında yapılır, ancak döküm yapılamazsa bir istisna atar.

Sadece önemli bir gerçeği eklemek için:

'As' anahtar kelimesi yalnızca referans türleriyle çalışır. Yapamazsın:

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

Bu gibi durumlarda döküm kullanmak zorundasınız.


Hatamı gösterdiğin için teşekkürler, haklısın. Cevabı düzenledim. Ayy üzgünüm.
Sergio Acosta

5

Şekil 2, türetilmiş bir türe döküm için faydalıdır.

Diyelim ki a bir hayvandır:

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

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

alacak bir atmalarını minimum beslemiştir.


2
@Chirs Moutray, bu her zaman mümkün değildir, özellikle de bir kütüphane ise.
yavaşladı

5

Bu sayfada yapılan deneylere göre: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(bu sayfada bazen "yasa dışı yönlendiren" hatalar görünüyor, bu yüzden varsa yenileyin)

Sonuç olarak, "as" operatörü normalde bir kadrodan daha hızlıdır. Bazen çok daha hızlı, bazen zar zor daha hızlı.

Peronsonally "olarak" bir şey de daha okunabilir.

Bu nedenle, hem daha hızlı hem de "daha güvenli" (istisna atmayacak) ve muhtemelen okunması daha kolay olduğu için, "her zaman" olarak kullanmanızı öneririm.


4

"(string) o", doğrudan yayın olmadığından InvalidCastException ile sonuçlanır.

"o as string", bir istisna oluşturulmak yerine s'nin boş bir başvuru olmasına neden olur.

"o.ToString ()" herhangi bir türden döküm değildir, nesne tarafından uygulanır ve dolayısıyla .net'teki her sınıf tarafından .net'teki her şey tarafından çağrıldığı sınıf bir dize döndürür.

Dizeye dönüştürmek için, someType'ın temelde çerçeveler temel türleri olan bir dizi kümeden biri olduğu Convert.ToString (someType instanceOfThatType) olduğunu da unutmayın.


3

Bir şey ekleyebilirseniz verilen tüm cevaplar iyidir: Doğrudan dizenin yöntemlerini ve özelliklerini (örneğin ToLower) kullanmak için yazamazsınız:

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

sadece şunu yazabilirsiniz:

((string)o).ToLower();

ancak bunun yerine şunu yazabilirsiniz:

(o as string).ToLower();

asSeçeneği (Bence en az) daha okunabilir.


(o as string) .ToLower () yapısı, as operatörünün amacını yener. Bu, string'e dönüştürülemediğinde boş bir referans istisnası atar.
james

@james - Ama as operatörünün tek amacının oyuncu başarısız olursa istisna atmak olduğunu kim söyledi? O'nun bir dize olduğunu biliyorsanız ve sadece (o as string).ToLower()birden çok kafa karıştırıcı parantez yerine daha temiz bir kod yazmak istiyorsanız .
BornToCode

as'ın amacı tam tersidir - oyuncu başarısız olduğunda istisnayı atmamalı, boş dönmelidir. Diyelim ki o null değerine sahip bir dize, o zaman ne olacak? İpucu - ToLower çağrınız başarısız olur.
james

@james - Haklısın, ama null olmayacağını kesin olarak bildiğim durumlar hakkında ve derleyicinin bu nesnenin yöntemlerine erişmeme izin vermesi için döküm yapmam gerekiyor?
BornToCode

1
bunu kesinlikle yapabilirsiniz, ancak tam olarak en iyi uygulama değildir, çünkü değerinizin boş olmadığından emin olmak için arayana veya harici sistemlere güvenmek istemezsiniz. C # 6 kullanıyorsanız (o string olarak)? Daha düşük().
james

3
string s = o as string; // 2

Çift döküm performans cezalarından kaçındığı için tercih edilir.


Merhaba Chris, bu cevabın içindeki bağlantı şimdi bir 404 ... Yerine koymak istediğiniz bir yedeğiniz olup olmadığından emin değil misiniz?
Matt

3

Görünüşe göre ikisi kavramsal olarak farklı.

Doğrudan Döküm

Türlerin kesinlikle birbiriyle ilişkili olması gerekmez. Her türlü aromada gelir.

  • Özel örtük / açık döküm: Genellikle yeni bir nesne oluşturulur.
  • Değer Türü Örtülü: Bilgi kaybetmeden kopyalayın.
  • Değer Türü Açık: Kopyalama ve bilgi kaybolabilir.
  • IS-A ilişkisi: Referans türünü değiştirin, aksi takdirde istisna atar.
  • Aynı tip: 'Döküm gereksizdir'.

Nesne başka bir şeye dönüştürülecek gibi geliyor.

AS operatörü

Türler arasında doğrudan bir ilişki vardır. De olduğu gibi:

  • Referans Türleri: IS-A ilişkisi Nesneler her zaman aynıdır, sadece referans değişir.
  • Değer Türleri: Kopyalama boksu ve null olabilecek türler.

Nesneyi farklı bir şekilde idare edecekmişsiniz gibi geliyor.

Örnekler ve 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

Operatör olarak aşağıdaki özelliklere dikkat çekmek istiyorum :

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

As operatörünün yalnızca referans dönüşümleri, boş değerli dönüşümler ve boks dönüşümleri gerçekleştirdiğini unutmayın. As operatörü, kullanıcı tanımlı dönüşümler gibi, dönüşüm ifadeleri kullanılarak gerçekleştirilmesi gereken diğer dönüşümleri gerçekleştiremez.


0

Potansiyel olarak boş olabilecek herhangi bir şeyin (herhangi bir türde) dize temsilini almaya çalışırken, aşağıdaki kod satırını tercih ederim. Kompakttır, ToString () işlevini çağırır ve null değerlerini doğru şekilde işler. O boşsa, s String.Empty içerecektir.

String s = String.Concat(o);

0

Kimse bundan bahsetmediği için, Java'ya anahtar kelime ile en yakın olanı şudur:

obj.GetType().IsInstanceOfType(otherObj)

0

string s = (string) o;Uygulamanızın mantıksal bağlamında stringgeçerli olan tek tür doğrudan döküm kullanın . Bu yaklaşımla BaşarısızlıkInvalidCastException prensibini alacak ve uygulayacaksınız . Mantığınız geçersiz türden daha fazla iletilmeye karşı korunacak veya operatör kullanılıyorsa NullReferenceException özelliğini alacaktır .as

Eğer mantık farklı tipte dökümler beklerse string s = o as string;ve kontrol ederse nullveya isoperatör kullanırsa .

C # 7.0'da yayınlamayı basitleştirmek ve bir Desen eşleşmesi olup olmadığını kontrol etmek için yeni harika özellik ortaya çıktı :

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

Aşağıdaki iki tür dönüştürme (döküm) C # biçiminde desteklenir:

|

(Özgeçmiş

• Verilen ifadede v'nin statik türünü c'ye dönüştürme

• Yalnızca dinamik v türü c veya c alt türü ise mümkündür

• Değilse, bir InvalidCastException atılır

|

v C olarak

• (c) v'nin ölümcül olmayan varyantı

• Böylece, verilen ifadede v'nin statik tipini c'ye dönüştürün

• Dinamik v türü c veya c alt türü değilse null değerini döndürür

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.