CLR'de 'as' anahtar kelimesini kullanarak veya yayınlama


387

Arayüzleri programlarken, çok fazla döküm veya nesne tipi dönüştürme yaptığımı buldum.

Bu iki dönüştürme yöntemi arasında bir fark var mı? Eğer öyleyse, bir maliyet farkı var mı veya bu programımı nasıl etkiler?

public interface IMyInterface
{
    void AMethod();
}

public class MyClass : IMyInterface
{
    public void AMethod()
    {
       //Do work
    }

    // Other helper methods....
}

public class Implementation
{
    IMyInterface _MyObj;
    MyClass _myCls1;
    MyClass _myCls2;

    public Implementation()
    {
        _MyObj = new MyClass();

        // What is the difference here:
        _myCls1 = (MyClass)_MyObj;
        _myCls2 = (_MyObj as MyClass);
    }
}

Ayrıca, tercih edilen yöntem "genel olarak" nedir?


Soruyu ilk etapta neden kullandığınıza dair küçük bir örnek ekleyebilir veya belki de yeni bir tane başlatabilir misiniz? Neden sadece ünite testi için oyuncu kadrosuna ihtiyacınız olduğunu merak ediyorum. Yine de bu sorunun kapsamı dışında olduğunu düşünüyorum.
Erik van Brakel

2
Muhtemelen bu ihtiyacı önlemek için birim testimi değiştirebilirim. Temelde, somut nesnemde arayüzde olmayan bir özelliğe sahip olduğum gerçeğine dayanıyor. O özelliği ayarlamam lazım ama gerçek hayat o özelliğin başka yollarla ayarlanmış olurdu. senin sorunun cevabı bu mu?
Frank V

Patrik Hägne zekice aşağıda işaret ettiği gibi, orada IS bir fark.
Neil

Yanıtlar:


519

Çizginin altındaki cevap 2008'de yazılmıştır.

C # 7 as, şimdi yazabileceğiniz gibi , operatörün yerini büyük ölçüde alan desen eşleştirme özelliğini tanıttı :

if (randomObject is TargetType tt)
{
    // Use tt here
}

Bundan ttsonra hala kapsamda olduğunu , ancak kesinlikle atanmadığını unutmayın. (O olduğu kesinlikle içinde atanan ifvücuda.) Eğer gerçekten her kapsamda olası değişkenlerin en küçük numaranın faaliyete geçirilmesi önem böylece Yani, eğer bazı durumlarda biraz sinir bozucu, hala kullanmak isteyebilirsiniz isalçıda izledi.


Şimdiye kadar cevapların hiçbirini düşünmüyorum (bu cevabı başlatırken!) Gerçekten hangisini kullanmaya değer olduğunu açıkladı.

  • Bunu yapma:

    // Bad code - checks type twice for no reason
    if (randomObject is TargetType)
    {
        TargetType foo = (TargetType) randomObject;
        // Do something with foo
    }

    Bu sadece iki kez kontrol etmekle kalmaz, aynı randomObjectzamanda yerel bir değişken yerine bir alan ise farklı şeyleri kontrol ediyor olabilir . Başka bir iş parçacığı randomObjectikisi arasındaki değeri değiştirirse, "if" in geçmesi ancak dökümün başarısız olması mümkündür .

  • Eğer randomObjectgerçekten gerektiği bir örneği olması TargetTypedaha sonra döküm, araçlar bir hata var ki, değilse, yani doğru çözümdür. Bu hemen bir istisna atar, bu da yanlış varsayımlar altında daha fazla iş yapılmadığı anlamına gelir ve istisna hata türünü doğru bir şekilde gösterir.

    // This will throw an exception if randomObject is non-null and
    // refers to an object of an incompatible type. The cast is
    // the best code if that's the behaviour you want.
    TargetType convertedRandomObject = (TargetType) randomObject;
  • Eğer randomObject olabilir bir örneği olması TargetTypeve TargetTypebir referans türüdür sonra böyle bir kodu kullanın:

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject
    }
  • Eğer randomObject olabilir bir örneği olması TargetTypeve TargetTypebir değer türüdür, o zaman kullanamaz asile TargetTypekendisi, ama biz bir null türü kullanabilirsiniz:

    TargetType? convertedRandomObject = randomObject as TargetType?;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject.Value
    }

    (Not: şu anda bu aslında + dökümden daha yavaş . Bence daha zarif ve tutarlı, ama işte başlıyoruz.)

  • Gerçekten dönüştürülen değeri gerekmez, ama sadece o olmadığını bilmek gerekiyorsa olan TargetType bir örneğini, daha sonra isoperatör senin arkadaşın. Bu durumda, TargetType'ın bir başvuru türü veya değer türü olması önemli değildir.

  • isFaydalı olduğu durumlarda jenerikleri içeren başka durumlar da olabilir (çünkü T'nin bir referans türü olup olmadığını bilemeyebilirsiniz, bu nedenle kullanamazsınız) ancak nispeten belirsizdirler.

  • Neredeyse kesinlikle isdeğer türü durum için kullandım, nullable türü ve asbirlikte kullanmayı düşünmeden :)


DÜZENLEME: Yukarıdaki özelliklerin hiçbirinin, null değeri olan bir değer türüne göre kutunun kaldırılmasının aslında daha yavaş olduğunu ancak tutarlı olduğunu belirten değer türü durumu dışında performanstan bahsetmediğini unutmayın.

Naasking'in cevabına göre, aşağıdaki kodda gösterildiği gibi, modern JIT'ler ile hem-ve-cast veya is-and-as kadar hızlıdır:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "x";
            values[i + 2] = new object();
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);
    }

    static void FindLengthWithIsAndCast(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string) o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)        
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

Dizüstü bilgisayarımda, bunların hepsi yaklaşık 60ms. Dikkat edilmesi gereken iki şey:

  • Aralarında önemli bir fark yok. (Aslında, as-plus-null-check'in kesinlikle daha yavaş olduğu durumlar vardır . Yukarıdaki kod aslında tip kontrolünü kolaylaştırır çünkü kapalı bir sınıf içindir; eğer bir arayüz kontrol ediyorsanız, denge biraz ipuçları as-plus-null-check lehine.)
  • Hepsi delicesine hızlı. Bu sadece olmaz gerçekten yapmayacak sürece kodunuzu darboğaz olmak bir şey sonradan değerlerle.

Performans hakkında endişelenmeyelim. Doğruluk ve tutarlılık konusunda endişelenelim.

I-ve-cast (veya is-and-as) değişkenlerle uğraşırken hem güvensiz olduğunu, çünkü değerin türü test ve döküm arasındaki başka bir iş parçacığı nedeniyle değişebileceğinden emin olun. Bu oldukça nadir bir durum olurdu - ama tutarlı bir şekilde kullanabileceğim bir sözleşmeyi tercih ederim.

Ben de o zaman null olarak kontrol endişelerin daha iyi ayrılmasını sağlar. Bir dönüşümü deneyen bir ifademiz ve ardından sonucu kullanan bir ifademiz var. İs-and-cast veya is-and-as bir test gerçekleştirir ve ardından değeri dönüştürmek için başka bir girişimde bulunur.

Diğer bir ifadeyle, herkes olur hiç yazma:

int value;
if (int.TryParse(text, out value))
{
    value = int.Parse(text);
    // Use value
}

Açıkçası daha ucuz bir şekilde olmasına rağmen, bu tür ve döküm ne yapıyor?


7
İşte IL açısından / as / döküm maliyeti: atalasoft.com/cs/blogs/stevehawley/archive/2009/01/30/…
bazalı

3
TargetObject hedef tip olabilirse , neden "is" ve cast kombinasyonunun kullanılması kötü bir uygulama olarak kabul edilir? Demek istediğim, daha yavaş bir kod üretir, ancak bu durumda niyetler, "targetObject boşsa bir şey yap" yerine "castObject targetType ise bir şey yap" gibi AS cast'tan daha açıktır. Ayrıca AS yan tümcesi gereksiz bir değişken oluşturur IF kapsamı dışında.
Valera Kolupaev

2
@Valera: İyi noktalar, ancak as / null testinin, niyetin neredeyse tüm C # geliştiricileri için açık olması gerektiği kadar deyimsel olduğunu düşünsem de. Ben şahsen is + cast dahil çoğaltma sevmiyorum. Aslında her iki eylemi de bir arada yapan bir tür "as-if" yapısı istiyorum. Onlar ... çok sık birlikte gidip
Jon Skeet

2
@Jon Skeet: geç kaldığım için üzgünüm. 128.000.000 öğeden oluşmaktadır.
Behrooz

2
C # 7 ile şunları yapabilirsiniz: if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}veya belgeleri kullanma switch/ case görme
WerWet

72

"as" , yayınlanamazsa NULL döndürür.

daha önce yayın yapmak bir istisna doğuracaktır.

Performans için bir istisna oluşturmak genellikle zaman içinde daha maliyetlidir.


3
İstisna yetiştirme daha pahalı olmakla birlikte, nesne doğru döküm edilebileceğini biliyorsanız, olarak (Anton tepkisini bakın) nedeniyle güvenlik kontrolünün daha fazla zaman gerektirir. Ancak, güvenlik kontrolünün maliyeti, sanırım oldukça küçüktür.

17
Potansiyel olarak bir istisna yaratmanın maliyeti dikkate alınması gereken bir faktördür, ancak genellikle doğru tasarımdır.
Jeffrey L Whitledge

@panesofglass - Referans türleri için, dönüşüm uyumluluğu hem çalışma hem de yayın için çalışma zamanında kontrol edilir, böylece bu faktör iki seçenek arasında ayrım yapmaz. (Bu böyle değilse, oyuncu kadrosu bir istisna oluşturamadı.)
Jeffrey L Whitledge

4
@Frank - Örneğin, pre-generics koleksiyonu kullanmanız gerekiyorsa ve API'nizdeki bir yöntem için Çalışanların listesi gerekiyorsa ve bazı joker bunun yerine bir Ürün listesi geçirirse, sinyal vermek için geçersiz bir döküm istisnası uygun olabilir arayüz gereksinimlerinin ihlali.
Jeffrey L Whitledge

27

İşte bazı IL karşılaştırmasıyla başka bir cevap. Sınıfı düşünün:

public class MyClass
{
    public static void Main()
    {
        // Call the 2 methods
    }

    public void DirectCast(Object obj)
    {
        if ( obj is MyClass)
        { 
            MyClass myclass = (MyClass) obj; 
            Console.WriteLine(obj);
        } 
    } 


    public void UsesAs(object obj) 
    { 
        MyClass myclass = obj as MyClass; 
        if (myclass != null) 
        { 
            Console.WriteLine(obj);
        } 
    }
}

Şimdi her yöntemin ürettiği IL'ye bakın. Op kodları sizin için bir şey ifade etmese bile, büyük bir fark görebilirsiniz - DirectCast yönteminde isinst ve ardından castclass çağırılıyor. Temelde bir yerine iki çağrı.

.method public hidebysig instance void  DirectCast(object obj) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  brfalse.s  IL_0015
  IL_0008:  ldarg.1
  IL_0009:  castclass  MyClass
  IL_000e:  pop
  IL_000f:  ldarg.1
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0015:  ret
} // end of method MyClass::DirectCast

.method public hidebysig instance void  UsesAs(object obj) cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (class MyClass V_0)
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  brfalse.s  IL_0010
  IL_000a:  ldarg.1
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0010:  ret
} // end of method MyClass::UsesAs

CastClass'a karşı isinst anahtar kelimesi

Bu blog yayını , bunu yapmanın iki yolu arasında iyi bir karşılaştırmaya sahiptir. Özeti:

  • Doğrudan bir karşılaştırmada, isinst, castclass'tan daha hızlıdır (sadece biraz olmasına rağmen)
  • Dönüşümün başarılı olduğundan emin olmak için kontroller yapmak gerektiğinde, ilk olarak castclass'tan önemli ölçüde daha hızlıydı
  • Isinst ve castclass kombinasyonu kullanılmamalıdır, çünkü bu en hızlı "güvenli" dönüşümden çok daha yavaştır (% 12'den daha yavaş)

Kişisel olarak her zaman As'ı kullanırım, çünkü okunması kolaydır ve .NET geliştirme ekibi (veya zaten Jeffrey Richter) tarafından önerilir


Döküm vs için açık bir açıklama arıyordum, bu cevap, ortak ara dili adım adım açıklamayı içerdiği için daha net hale getiriyor. Teşekkürler!
Morse

18

İkisi arasındaki daha ince farklardan biri, "as" anahtar kelimesinin bir döküm operatörü söz konusu olduğunda döküm için kullanılamamasıdır:

public class Foo
{
    public string Value;

    public static explicit operator string(Foo f)
    {
        return f.Value;
    }

}

public class Example
{
    public void Convert()
    {
        var f = new Foo();
        f.Value = "abc";

        string cast = (string)f;
        string tryCast = f as string;
    }
}

Bu "(" anahtar kelimeler döküm operatörleri dikkate almaz çünkü son satırda (önceki sürümlerde yaptı sanmıyorum) derlemek değil. Çizgi string cast = (string)f;olsa iyi çalışıyor.


12

olarak o dönüşüm dönen gerçekleştiremezsiniz eğer hiç bir istisna atar null adlı (yerine olarak referans tipleri yalnızca çalışır). Yani as kullanmak temel olarak

_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;

Öte yandan, C tarzı dökümler, dönüşüm mümkün olmadığında bir istisna atar.


4
Eşdeğer, evet, ama aynı değil. Bu şekilde olduğu gibi daha fazla kod üretir.
Süpürgelik

10

Sorunuza gerçekten bir cevap değil, bence önemli bir nokta.

Bir arayüze programlama yapıyorsanız yayın yapmanıza gerek yoktur. Umarım bu oyuncular çok nadirdir. Değilse, muhtemelen bazı arayüzlerinizi yeniden düşünmeniz gerekir.


Döküm, şimdiye kadar çoğunlukla Birim Testlerim için gerekliydi, ancak getirdiğin için teşekkür ederim. Bunun üzerinde çalışırken bunu aklımda tutacağım.
Frank V

Kurbağa ile anlaştığım için, birim test unsurunun sizin için dökümle neden alakalı olduğunu merak ediyorum @Frank V Döküm için bir ihtiyaç olduğunda, denediğinizi önerdiği için genellikle yeniden tasarlamaya veya yeniden düzenleyiciye ihtiyaç vardır. farklı sorunların yönetilmesi gereken yerlerde farklı ayakkabı çekmeye.
Senatör

@TheSenator Bu soru 3 yaşın üzerinde, bu yüzden gerçekten hatırlamıyorum. Ama muhtemelen birim testlerinde bile arayüzleri agresif bir şekilde kullanıyordum. Muhtemelen fabrika desenini kullanıyordum ve test edilecek hedef nesneler üzerinde bir genel kurucuya erişemedim.
Frank V

9

Lütfen Jon Skeet'in tavsiyelerini dikkate almayın, yeniden: test ve döküm modelinden kaçının, örn .:

if (randomObject is TargetType)
{
    TargetType foo = randomObject as TargetType;
    // Do something with foo
}

Bunun bir oyuncu kadrosundan ve bir null testten daha pahalı olduğu fikri bir mittir :

TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
    // Do stuff with convertedRandomObject
}

Bu işe yaramayan bir mikro optimizasyon. Bazı gerçek testler yaptım ve test ve cast aslında cast ve null karşılaştırmasından daha hızlı ve bu da daha güvenlidir, çünkü eğer cast almanız gerekiyorsa kapsam dışında boş bir referansa sahip olma olasılığınız yoktur. başarısız.

Test etmenin daha hızlı veya en azından daha yavaş olmamasının bir nedenini istiyorsanız, basit ve karmaşık bir neden vardır.

Basit: saf derleyiciler bile test ve döküm gibi iki benzer işlemi tek bir test ve dalda birleştirir. cast-and-null-test iki test ve bir dal, biri tip testi ve hata durumunda null değerine dönüştürme, biri de null kontrolün kendisi için zorlayabilir. En azından, her ikisi de tek bir test ve dal için optimize edecekler, bu nedenle test ve döküm, cast ve null testinden ne daha yavaş ne de daha hızlı olacaktır.

Karmaşık: test ve döküm neden daha hızlıdır: cast ve null testi, derleyicinin canlılık için izlemesi gereken dış kapsama başka bir değişken ekler ve kontrolünüzün ne kadar karmaşık olduğuna bağlı olarak bu değişkeni optimize edemeyebilir. akış. Bunun tersine, test etme ve yayınlama, yalnızca sınırlandırılmış bir kapsamda yeni bir değişken sunar; böylece derleyici, kapsamdan çıktıktan sonra değişkenin öldüğünü bilir ve böylece kayıt ayırmayı daha iyi optimize edebilir.

Bu yüzden lütfen LÜTFEN bu "cast-and-null-test test ve cast" dan daha iyi DIE tavsiyesine izin verin. LÜTFEN. test etme ve yayınlama hem daha güvenli hem de daha hızlıdır.


7
@naasking: İki kez test ederseniz (ilk snippet'inize göre), alan veya refparametre ise, türün iki test arasında değişme olasılığı vardır . Yerel değişkenler için güvenlidir, ancak alanlar için güvenli değildir. Kıyaslamalarınızı çalıştırmak isterim, ancak blog yayınınızda verdiğiniz kod tam değil. Mikro optimizasyon yapmamaya katılıyorum, ancak değeri iki kez kullanmanın "as" ve bir geçersizlik testi kullanmaktan daha okunabilir veya zarif olduğunu düşünmüyorum. (Kesinlikle bir "btw" yerine "gibi" yerine düz bir döküm kullanırdım.)
Jon Skeet

5
Ayrıca neden daha güvenli olduğunu da anlamıyorum. Aslında neden daha az güvenli olduğunu gösterdim . Elbette, kapsamda boş olabilecek bir değişken elde edersiniz, ancak bunu sonraki "if" bloğunun kapsamı dışında kullanmaya başlamadığınız sürece sorun olmaz. Ortaya koyduğum güvenlik endişesi (değerlerini değiştiren alanların etrafında) gösterilen kodla ilgili gerçek bir endişedir - güvenlik endişeniz geliştiricilerin diğer kodlarda gevşek olmasını gerektirir.
Jon Skeet

1
/ Cast ya da / cast olarak işaret etmek için +1 gerçekte daha yavaş değildir, unutmayın. Tam bir testi kendim çalıştırmak olan, bunu bildiğim kadarıyla gördüğünüz gibi hiç fark etmez teyit edebilir - ve açıkçası bir çalıştırabilir almaz çok küçük bir zaman içinde atmalarını sayısını. Cevabımı tam kodla güncelleyeceğim.
Jon Skeet

1
Gerçekten de, eğer bağlanma yerel değilse, bir TOCTTOU hatası (kullanım-zamanı-kontrol-zamanı) şansı vardır. Neden daha güvenli olduğuna gelince, yerlileri bir nedenden dolayı tekrar kullanmaktan hoşlanan birçok genç geliştiriciyle çalışıyorum. cast-and-null benim için çok büyük bir tehlike. Kodumu bu şekilde tasarlamadığım için asla TOCTTOU ile karşılaşmadım. Çalışma zamanı test hızına gelince, sanal sevkıyattan bile daha hızlıdır [1]! Re: kod, döküm testi için kaynak bulabilir miyim göreceğim. [1] higherlogics.blogspot.com/2008/10/…
naasking

1
@naasking: Yerel yeniden kullanım sorununa hiç rastlamadım - ama kod incelemesinde daha ince TOCTTOU hatalarından daha kolay olduğunu söyleyebilirim. Ayrıca, mühürlü bir sınıf yerine arayüzler için kendi kontrol testimi tekrar çalıştırdığımı ve performansı o zaman null olarak kontrol lehine döndürdüğümü belirtmek gerekir ... ama dediğim gibi performans değil bu yüzden burada herhangi bir yaklaşımı tercih etmem.
Jon Skeet

4

Oyuncular başarısız olursa, 'as' anahtar kelimesi bir istisna oluşturmaz; bunun yerine değişkeni null (veya değer türleri için varsayılan değerine) olarak ayarlar.


3
Değer türleri için varsayılan değer yok. Çünkü döküm değer tipleri için kullanılamaz.
Patrik Hägne

2
'As' anahtar kelimesi aslında değer türlerinde çalışmaz, bu nedenle her zaman null değerine ayarlanır.
Erik van Brakel

4

Bu sorunun yanıtı değil, sorunun kod örneğine yapılan yorumdur:

Genellikle bir Nesneyi örneğin IMyInterface uygulamasından MyClass uygulamasına dökmeniz gerekmez. Arabirimlerle ilgili en iyi şey, bir nesneyi bir arabirimi uygulayan girdi olarak alırsanız, ne tür bir nesne aldığınızla ilgilenmemenizdir.

MyClass'a IMyInterface uygularsanız, MyClass türünde bir nesne aldığınızı varsayarsınız ve IMyInterface'i kullanmanın bir anlamı yoktur, çünkü kodunuzu IMyInterface'i uygulayan diğer sınıflarla beslerseniz, kodunuzu kıracaktır ...

Şimdi, tavsiyem: arayüzleriniz iyi tasarlanmışsa, çok fazla daktilodan kaçınabilirsiniz.


3

asOperatör sadece aşırı edilemez, referans tipleri kullanılabilir ve bu dönecektirnull operasyon başarısız olursa. Asla bir istisna atmaz.

Döküm herhangi bir uyumlu tipte kullanılabilir, aşırı yüklenebilir ve işlem başarısız olursa bir istisna atar.

Hangi seçeneğin kullanılacağı koşullara bağlıdır. Öncelikle, başarısız bir dönüşüme istisna atmak isteyip istemediğiniz meselesidir.


1
'as', ilginç bir örüntü sağlayan boş değer değer türlerinde de kullanılabilir. Kod için cevabıma bakın.
Jon Skeet

1

Cevabım sadece türü kontrol etmediğimiz ve dökümden sonra null'ları kontrol etmediğimiz durumlarda hız ile ilgilidir. Jon Skeet'in koduna iki test daha ekledim:

using System;
using System.Diagnostics;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];

        for (int i = 0; i < Size; i++)
        {
            values[i] = "x";
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);

        FindLengthWithCast(values);
        FindLengthWithAs(values);

        Console.ReadLine();
    }

    static void FindLengthWithIsAndCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string)o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
    static void FindLengthWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = (string)o;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

Sonuç:

Is and Cast: 30000000 : 88
Is and As: 30000000 : 93
As and null check: 30000000 : 56
Cast: 30000000 : 66
As: 30000000 : 46

Hıza odaklanmaya çalışmayın (yaptığım gibi) çünkü tüm bunlar çok hızlı.


Benzer şekilde, testlerimde, asdönüşümün (hata kontrolü olmadan) dökümden yaklaşık% 1-3 daha hızlı olduğunu gördüm (100 milyon yinelemede 550 ms'ye karşılık 540 ms). İkisi de başvurunuzu yapmaz veya bozmaz.
palswim

1

Burada zaten ortaya çıkanların yanı sıra, açık döküm arasında dikkat çekmeye değer olduğunu düşündüğüm pratik bir farkla karşılaştım.

var x = (T) ...

asoperatörün kullanımına karşı .

İşte örnek:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GenericCaster<string>(12345));
        Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
        Console.WriteLine(GenericCaster<double>(20.4));

        //prints:
        //12345
        //null
        //20.4

        Console.WriteLine(GenericCaster2<string>(12345));
        Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");

        //will not compile -> 20.4 does not comply due to the type constraint "T : class"
        //Console.WriteLine(GenericCaster2<double>(20.4));
    }

    static T GenericCaster<T>(object value, T defaultValue = default(T))
    {
        T castedValue;
        try
        {
            castedValue = (T) Convert.ChangeType(value, typeof(T));
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }

    static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
    {
        T castedValue;
        try
        {
            castedValue = Convert.ChangeType(value, typeof(T)) as T;
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }
}

Alt satır: GenericCaster2, yapı türleriyle çalışmaz. GenericCaster olacak.


1

.NET Framework 4.X'i hedefleyen Office PIA'larını kullanırsanız, as anahtar sözcüğünü kullanmalısınız , aksi takdirde derlenmez.

Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;

Ancak .NET 2.0'ı hedeflerken döküm tamam:

Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);

.NET 4.X hedeflenirken hatalar şunlardır:

hata CS0656: Derleyici eksik üyesi gerekli 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

hata CS0656: Derleyici eksik üyesi gerekli 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'


0

asAnahtar kelime dönüşüm başarısız olursa o bir istisna değildir majör fark ile uyumlu başvuru türleri arasındaki açık bir döküm gibi çalışır. Aksine, hedef değişkende boş bir değer verir. İstisnalar performans açısından çok pahalı olduğundan, daha iyi bir döküm yöntemi olarak kabul edilir.


Aynı değil, biri CastClass ve diğer ILI kodunda IsInst çağırır.
Jenix

0

Ne seçeceğiniz, neyin gerekli olduğuna bağlıdır. Açık seçimleri tercih ederim

IMyInterface = (IMyInterface)someobj;

çünkü eğer nesne IMyInterface tipinde olmalı ve değilse - kesinlikle problem. Hatayı mümkün olduğunca erken almak daha iyidir, çünkü yan etkisini düzeltmek yerine tam hata düzeltilecektir.

Ancak objectparametre olarak kabul eden yöntemlerle uğraşırsanız, herhangi bir kodu çalıştırmadan önce tam türünü kontrol etmeniz gerekir. Böyle bir durumda asyararlı olabilir, böylece önleyebilirsiniz InvalidCastException.


0

Bu, "as" kullandıktan sonra null olup olmadığını kontrol etmek istiyor musunuz yoksa uygulamanızın bir istisna atmasını mı tercih edersiniz?

Temel kuralım, değişkenin her zaman bir döküm kullanmamı beklediğim türde olmasını bekliyorsam. Değişkenin istediğim şeye dökülmemesi mümkünse ve null'ları as kullanarak işlemeye hazırım, olarak kullanacağım.



0

OP'nin problemi belirli bir döküm durumu ile sınırlıdır. Başlık çok daha fazla durumu kapsamaktadır.
Şu anda düşünebileceğim tüm ilgili döküm durumlarına genel bir bakış:

private class CBase
{
}

private class CInherited : CBase
{
}

private enum EnumTest
{
  zero,
  one,
  two
}

private static void Main (string[] args)
{
  //########## classes ##########
  // object creation, implicit cast to object
  object oBase = new CBase ();
  object oInherited = new CInherited ();

  CBase oBase2 = null;
  CInherited oInherited2 = null;
  bool bCanCast = false;

  // explicit cast using "()"
  oBase2 = (CBase)oBase;    // works
  oBase2 = (CBase)oInherited;    // works
  //oInherited2 = (CInherited)oBase;   System.InvalidCastException
  oInherited2 = (CInherited)oInherited;    // works

  // explicit cast using "as"
  oBase2 = oBase as CBase;
  oBase2 = oInherited as CBase;
  oInherited2 = oBase as CInherited;  // returns null, equals C++/CLI "dynamic_cast"
  oInherited2 = oInherited as CInherited;

  // testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
  bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ());    // true
  bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ());    // true
  bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ());    // false
  bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ());    // true

  //########## value types ##########
  int iValue = 2;
  double dValue = 1.1;
  EnumTest enValue = EnumTest.two;

  // implicit cast, explicit cast using "()"
  int iValue2 = iValue;   // no cast
  double dValue2 = iValue;  // implicit conversion
  EnumTest enValue2 = (EnumTest)iValue;  // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')

  iValue2 = (int)dValue;   // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
  dValue2 = dValue;
  enValue2 = (EnumTest)dValue;  // underlying type is int, so "1.1" beomces "1" and then "one"

  iValue2 = (int)enValue;
  dValue2 = (double)enValue;
  enValue2 = enValue;   // no cast

  // explicit cast using "as"
  // iValue2 = iValue as int;   error CS0077: The as operator must be used with a reference type or nullable type
}
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.