[c#] ‘is’대 null 검사로 시도 캐스트

Resharper가 이것을 돌리라고 제안하는 것을 발견했습니다.

if (myObj.myProp is MyType)
{
   ...
}

이것으로 :

var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
   ...
}

왜 이러한 변화를 제안합니까? 저는 Resharper가 최적화 변경과 코드 감소 변경을 제안하는 데 익숙하지만, 이것은 내 단일 문장을 두 줄로 바꾸고 싶은 것처럼 느껴집니다.

MSDN 에 따르면 :

이다 발현 다음 두 조건이 모두 충족 될 경우는 true로 평가

식이 null이 아닙니다. expression은 유형 으로 캐스트 될 수 있습니다 . 즉, 양식의 캐스트 표현식은 (type)(expression)예외를 발생시키지 않고 완료됩니다.

isnull 검사를 위해 다른 지역 변수를 명시 적으로 만들 필요없이 한 줄에 정확히 동일한 검사를 수행 하지 않거나 잘못 읽었 습니까?



답변

캐스트가 하나뿐이기 때문입니다. 이것을 비교하십시오 :

if (myObj.myProp is MyType) // cast #1
{
    var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time
                                         // before using it as a MyType
    ...
}

이에:

var myObjRef = myObj.myProp as MyType; // only one cast
if (myObjRef != null)
{
    // myObjRef is already MyType and doesn't need to be cast again
    ...
}

C # 7.0은 패턴 일치를 사용하여보다 간결한 구문을 지원합니다 .

if (myObj.myProp is MyType myObjRef)
{
    ...
}


답변

가장 좋은 방법은 다음과 같은 패턴 일치를 사용하는 것입니다.

if (value is MyType casted){
    //Code with casted as MyType
    //value is still the same
}
//Note: casted can be used outside (after) the 'if' scope, too


답변

실제로 벨트 아래에서 일어나는 일에 대한 정보는 아직 없습니다. 이 예를 살펴보십시오.

object o = "test";
if (o is string)
{
    var x = (string) o;
}

이것은 다음 IL로 변환됩니다.

IL_0000:  nop
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  ldnull
IL_000E:  cgt.un
IL_0010:  stloc.1
IL_0011:  ldloc.1
IL_0012:  brfalse.s   IL_001D
IL_0014:  nop
IL_0015:  ldloc.0     // o
IL_0016:  castclass   System.String
IL_001B:  stloc.2     // x
IL_001C:  nop
IL_001D:  ret   

여기서 중요한 것은 isinstcastclass호출입니다. 둘 다 상대적으로 비쌉니다. 그것을 대안과 비교하면 isinst확인 만 수행한다는 것을 알 수 있습니다 .

object o = "test";
var oAsString = o as string;
if (oAsString != null)
{

}

IL_0000:  nop
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  stloc.1     // oAsString
IL_000E:  ldloc.1     // oAsString
IL_000F:  ldnull
IL_0010:  cgt.un
IL_0012:  stloc.2
IL_0013:  ldloc.2
IL_0014:  brfalse.s   IL_0018
IL_0016:  nop
IL_0017:  nop
IL_0018:  ret  

또한 언급 할 가치가있는 것은 값 유형이 다음 unbox.any대신 사용 한다는 것입니다 castclass.

object o = 5;
if (o is int)
{
    var x = (int)o;
}

IL_0000:  nop
IL_0001:  ldc.i4.5
IL_0002:  box         System.Int32
IL_0007:  stloc.0     // o
IL_0008:  ldloc.0     // o
IL_0009:  isinst      System.Int32
IL_000E:  ldnull
IL_000F:  cgt.un
IL_0011:  stloc.1
IL_0012:  ldloc.1
IL_0013:  brfalse.s   IL_001E
IL_0015:  nop
IL_0016:  ldloc.0     // o
IL_0017:  unbox.any   System.Int32
IL_001C:  stloc.2     // x
IL_001D:  nop
IL_001E:  ret   

그러나 이것이 반드시 여기에서 볼 수있는 것처럼 더 빠른 결과로 변환되는 것은 아닙니다 . 캐스트가 될하는 데 사용하지만, 최대한 빨리 수행 할 것 같습니다 :하지만 그 질문은 질문 이후 개선되었을 것 같다 aslinq현재 약 3 배 빠른 속도입니다.


답변

Resharper 경고 :

"Type check and direct cast can be replaced with try cast and check for null"

둘 다 작동하며 코드가 더 잘 맞는지에 따라 다릅니다. 제 경우에는 그 경고를 무시합니다.

//1st way is n+1 times of casting
if (x is A) ((A)x).Run();
else if (x is B) ((B)x).Run();
else if (x is C) ((C)x).Run();
else if (x is D) ((D)x).Run();
//...
else if (x is N) ((N)x).Run();
//...
else if (x is Z) ((Z)x).Run();

//2nd way is z times of casting
var a = x as Type A;
var b = x as Type B;
var c = x as Type C;
//..
var n = x as Type N;
//..
var z = x as Type Z;
if (a != null) a.Run();
elseif (b != null) b.Run();
elseif (c != null) c.Run();
...
elseif (n != null) n.Run();
...
elseif (x != null) x.Run();

내 코드에서 두 번째 방법은 더 길고 성능이 떨어집니다.


답변

나에게 이것은 그 유형이 될 가능성이 무엇인지에 달려 있습니다. 개체가 대부분의 경우 해당 유형의 경우 전면 캐스트를 수행하는 것이 확실히 더 효율적입니다. 그 유형이 가끔씩 만 발생한다면 우선 is로 확인하는 것이 더 최적 일 수 있습니다.

지역 변수를 만드는 비용은 유형 검사 비용에 비해 매우 미미합니다.

가독성과 범위는 일반적으로 나에게 더 중요한 요소입니다. 저는 ReSharper에 동의하지 않으며 그 이유만으로 “is”연산자를 사용합니다. 이것이 진정한 병목이라면 나중에 최적화하십시오.

( myObj.myProp is MyType이 기능에서 한 번만 사용한다고 가정합니다 )


답변

두 번째 변경 사항도 제안해야합니다.

(MyType)myObj.myProp

으로

myObjRef

이렇게하면 원래 코드와 비교하여 속성 액세스 및 캐스트가 절약됩니다. 그러나로 변경 한 후에 만 ​​가능 is합니다 as.


답변

이것은 myObjRef 인 myObj.myProp의 강력한 형식 버전을 만드는 것입니다. 그런 다음 블록에서이 값을 참조 할 때와 캐스트를 수행해야 할 때 사용해야합니다.

예를 들면 다음과 같습니다.

myObjRef.SomeProperty

이것보다 낫다 :

((MyType)myObj.myProp).SomeProperty