20
May

is, as, != null

Po diskuzi v postu if, else, return jsem si uvědomil, že mám v kapse ještě jeden případ, který je podobně sporný-zajímavý. Tyto dvě konstrukce vídávám a opět v zásadě stejné – nebo ne?

Foo x = (y as Foo);
if (x != null)
{
  x...
}
else
{
  // <error>
}

resp. (samozřejmě můžeme i přiřadit do x)

if (y is Foo)
{
  (y as Foo)...
}
else
{
  // <error>
}

Osobně používam druhý způsob, opět spíše pocitově něž pro nějaký fakt (myslím, že z toho is je výsledek mého snažení lépe čitelný). A co vy? Případně máte nějaký argument proč používat jedno nebo druhé?

There's 14 Comments So Far

  • DeaLer
    May 20th, 2009 at 22:20

    První varianta je asi korektnější, protože zde probíhá přetypování pouze jednou, ale osobně používám tu druhou. Jen malá drobnost -> u druhé varianty je zbytečné používát přetypování s pomocí klíčového slova “as”, protože zde y nemůže být null. Pokud by tomu tak bylo, nevyhověla by podmínka.

  • cincura.net
    May 20th, 2009 at 22:29

    Jo mas pravdu, psal jsem to z fleku. Tam by slo primo ((Foo)y)…U toho druheho je pretypovani taky jen jednou. Nicmene *asi* != null bude levnejsi nez is. Nedelal nekdo bechmark?

  • mstr
    May 20th, 2009 at 22:54

    teoreticky je vas druhy zpusob pomalejsi, provadi se tam dve kontroly na typ, zatimco v prvnim jen jedna a druha kontrola je na null a tedy teoreticky rychlejsi.Rozebrano je to na strance 101 a 102 knizky CLR via C#.

  • DeaLer
    May 20th, 2009 at 23:02

    JJ. Pokud si dobře vzpomínám, při použití “is” dochází také k přetypování a pokud nedojde k InvalidCastException, je výsledek true. Ale stejně mě to neodrazuje od používání této varianty.

  • DeaLer
    May 20th, 2009 at 23:07

    Co to píšu za blbosti :) Výsledek je false.

  • DeaLer
    May 20th, 2009 at 23:08

    No tohle už je vážný. Raději tyhle dva smaž … díky :)

  • jachymko
    May 20th, 2009 at 23:46

    ja pouzivamvar x = a as T;if (x != null){}proc pretypovavat dvakrat…?

  • Tomas Petricek
    May 21st, 2009 at 01:56

    Teoreticky vzato bude asi spravnejsi pouzit “as” pouze jednou (protoze pretypovani v .NETu nejaky rychly run-time check vzdycky vyzaduje). V praxi bych tipoval ze to nebude nijak vyznamny rozdil, pokud vubec (JIT by to mohl snadno optimalizovat kdyby chtel… a dost mozna to kvuli nejake vymyslenosti v Dynamic Language Runtime optimalizovat musi).Ale nerekl bych ze “is” bude provadet pretypovani a zachytavat vyjimku, protoze vyjimky jsou dost pomala zalezitost. Spis bych tipoval ze “a is A” znamena “(a as A) != null” a ze to pouziva opcode “isinst”. Mimochodem, technicky vzato by mohlo jit napsat neco jako toto:Case(value, (T1 x) => { .. pripad kdy value je T1 .. }, (T2 x) => { .. pripad kdy value je T2 .. });

  • pbouda
    May 21st, 2009 at 08:56

    Osobně se mi více líbí druhý způsob, protože je čitelnější, ale stejně používám spíše ten první, protože když to budu stejně přetypovávat, tak proč to neudělat rovnou.Zcela zaměnitelné ty dva způsoby ale nejsou, protože “is” lze aplikovat jak na refernce type, tak na value type, zatímco “as” pouze na value type, takže pokud proměnná může být struct, pak je potřeba druhý způsob s modifikací (Foo)y místo y as Foo.

  • cincura.net
    May 21st, 2009 at 09:06

    Prejne tak Tomasi. Nakonec jsem se tomu podival na zoubek, teda na IL. A oba pristupy generuji stejne sekvence prikazu. Jedine u toho druheho se uz nemusi potom treba prirazovat do promenne.Vypadne z toho: IL_0000: ldarg.0 IL_0001: isinst ConsoleApplication1.Foo IL_0006: brfalse.s IL_000a IL_0008: ldc.i4.1 IL_0009: ret IL_000a: ldc.i4.0 IL_000b: ret Takze uz opravdu zbyva jen prakticke hledisko.

  • cincura.net
    May 21st, 2009 at 09:08

    2pbouda: Jasny. Nepsal jsem, ze je to uplne stejne. Ja vzdy davam prednost nejprve citelnosti, takze proto volim is.

  • Jakub Šturc
    May 21st, 2009 at 09:59

    Ja by som to napisal takto:1 Foo x = (y as Foo);2 if (x == null)3 {4 // ;5 }6 x…Preco hlavny scenar behu programu schovavat niekam do else vetvy?

  • T
    May 21st, 2009 at 12:36

    @jirka,tomas:Jednoznacne prva verzia bude rychlejsia, pretoze sa len raz zavola isinst(tyke check), v druhom pripade dvakrat bez ohladu na zapnutu optimializaciu, ak ma niekto dokaz, ze to je inak, tak prosim sem s tym aj so zdrojakom…static void Main(string[] args){ object bar = new Foo(); //upcast Foo bar2 = bar as Foo; if(bar2 == null) throw new InvalidCastException(“Foo”); bar2.Test(); if (!(bar is Foo)) throw new InvalidCastException(“Foo”); Foo bar3= bar as Foo; bar3.Test();}neoptimalizavany byte kode:.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 82 (0×52) .maxstack 2 .locals init ([0] object bar, [1] class ConsoleApplication2.Program/Foo bar2, [2] class ConsoleApplication2.Program/Foo bar3, [3] bool CS$4$0000) IL_0000: nop IL_0001: newobj instance void ConsoleApplication2.Program/Foo::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: isinst ConsoleApplication2.Program/Foo IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: ldnull IL_0010: ceq IL_0012: ldc.i4.0 IL_0013: ceq IL_0015: stloc.3 IL_0016: ldloc.3 IL_0017: brtrue.s IL_0024 IL_0019: ldstr “Foo” IL_001e: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0023: throw IL_0024: ldloc.1 IL_0025: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_002a: nop IL_002b: ldloc.0 IL_002c: isinst ConsoleApplication2.Program/Foo IL_0031: ldnull IL_0032: cgt.un IL_0034: stloc.3 IL_0035: ldloc.3 IL_0036: brtrue.s IL_0043 IL_0038: ldstr “Foo” IL_003d: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0042: throw IL_0043: ldloc.0 IL_0044: isinst ConsoleApplication2.Program/Foo IL_0049: stloc.2 IL_004a: ldloc.2 IL_004b: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0050: nop IL_0051: ret} // end of method Program::Mainoptimalizacia:.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 66 (0×42) .maxstack 2 .locals init ([0] object bar, [1] class ConsoleApplication2.Program/Foo bar2, [2] class ConsoleApplication2.Program/Foo bar3) IL_0000: newobj instance void ConsoleApplication2.Program/Foo::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: isinst ConsoleApplication2.Program/Foo IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: brtrue.s IL_001b IL_0010: ldstr “Foo” IL_0015: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_001a: throw IL_001b: ldloc.1 IL_001c: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0021: ldloc.0 IL_0022: isinst ConsoleApplication2.Program/Foo IL_0027: brtrue.s IL_0034 IL_0029: ldstr “Foo” IL_002e: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0033: throw IL_0034: ldloc.0 IL_0035: isinst ConsoleApplication2.Program/Foo IL_003a: stloc.2 IL_003b: ldloc.2 IL_003c: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0041: ret} // end of method Program::Mainbtw.+@Jakub ŠturcCo je zmyslom else { //error } sekvencie? thrownutie expection? Chceme a mame dovod pridat vyssiu uroven popisnosti ako ziskame pri Foo x = (Foo) y; ktore zabezpeci trownutie InvalidCastException?Ak ano, ma zmysel kod hore v bloku. Ak nie, tak Foo x = (Foo) y; je to najrozumnejsie na co sa cela diskusia svrkne.

  • T
    May 22nd, 2009 at 09:35

    Neviem, preco sa neobublikoval moj vcerajsi prispevok…ale v skratke:Vzhladom na pouzitie as aj bez chybajucej delaracie y ide o upcast scenar. 1. prva verzia bude rychlejsia bez ohladu na zapnutu optimalizaciu , v druhej sa vola isinst dvakrat, v prvej raz…(vid dolu)2. Unika mi zmysel kodu – je zmyslom error vetvry generovanie viac popisnej exception, ako clovek ziska ked cely kod nahradi jednodo pouzitim cast operatora?… Foo x= (Foo) y; dokaz:object bar = new Foo(); //upcast Foo bar2 = bar as Foo; if(bar2 == null) throw new InvalidCastException(“Foo”); bar2.Test(); if (!(bar is Foo)) throw new InvalidCastException(“Foo”); Foo bar3= bar as Foo; bar3.Test(); if(bar is Foo) (bar as Foo).Test(); else throw new InvalidCastException(“Foo”);.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 82 (0×52) .maxstack 2 .locals init ([0] object bar, [1] class ConsoleApplication2.Program/Foo bar2, [2] class ConsoleApplication2.Program/Foo bar3, [3] bool CS$4$0000) IL_0000: nop IL_0001: newobj instance void ConsoleApplication2.Program/Foo::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: isinst ConsoleApplication2.Program/Foo IL_000d: stloc.1 IL_000e: ldloc.1 IL_000f: ldnull IL_0010: ceq IL_0012: ldc.i4.0 IL_0013: ceq IL_0015: stloc.3 IL_0016: ldloc.3 IL_0017: brtrue.s IL_0024 IL_0019: ldstr “Foo” IL_001e: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0023: throw IL_0024: ldloc.1 IL_0025: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_002a: nop IL_002b: ldloc.0 IL_002c: isinst ConsoleApplication2.Program/Foo IL_0031: ldnull IL_0032: cgt.un IL_0034: stloc.3 IL_0035: ldloc.3 IL_0036: brtrue.s IL_0043 IL_0038: ldstr “Foo” IL_003d: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0042: throw IL_0043: ldloc.0 IL_0044: isinst ConsoleApplication2.Program/Foo IL_0049: stloc.2 IL_004a: ldloc.2 IL_004b: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0050: nop IL_0051: ret} // end of method Program::Main.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 66 (0×42) .maxstack 2 .locals init ([0] object bar, [1] class ConsoleApplication2.Program/Foo bar2, [2] class ConsoleApplication2.Program/Foo bar3) IL_0000: newobj instance void ConsoleApplication2.Program/Foo::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: isinst ConsoleApplication2.Program/Foo IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: brtrue.s IL_001b IL_0010: ldstr “Foo” IL_0015: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_001a: throw IL_001b: ldloc.1 IL_001c: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0021: ldloc.0 IL_0022: isinst ConsoleApplication2.Program/Foo IL_0027: brtrue.s IL_0034 IL_0029: ldstr “Foo” IL_002e: newobj instance void [mscorlib]System.InvalidCastException::.ctor(string) IL_0033: throw IL_0034: ldloc.0 IL_0035: isinst ConsoleApplication2.Program/Foo IL_003a: stloc.2 IL_003b: ldloc.2 IL_003c: callvirt instance void ConsoleApplication2.Program/Foo::Test() IL_0041: ret} // end of method Program::Main

Share your thoughts, leave a comment!