null 검사가없는 경우에도 캐스트 대신 "as"를 사용하는 것이 합리적입니까? [닫은]


351

개발 블로그, 온라인 코드 예제 및 (최근) 책에서도 다음과 같은 코드에 대해 계속 걸림돌이됩니다.

var y = x as T;
y.SomeMethod();

또는 더 나쁜 :

(x as T).SomeMethod();

그건 말이되지 않습니다. x유형 이 확실하다면 T직접 캐스트를 사용해야합니다 (T)x.. 확실하지 않은 경우 일부 작업을 수행하기 전에 사용할 수 as있지만 확인해야 null합니다. 위의 코드가하는 것은 (유용한) InvalidCastException를 (유용하지 않은)로 바꾸는 것 NullReferenceException입니다.

이것이 as키워드 의 명백한 남용이라고 생각하는 유일한 사람 입니까? 아니면 내가 명백한 것을 그리워하고 위의 패턴이 실제로 의미가 있습니까?


56
보는 것이 더 재미있을 것입니다 (S로 키스) .SteveIsSuchA (); 그러나 나는 그 학대에 동의합니다.
SwDevMan81

5
쓰기보다 훨씬 시원 ((T)x).SomeMethod()하지 않습니까? ;) (그냥 농담입니다. 물론입니다!)
Lucero

8
@ P Daddy 나는 동의하지 않고, 완벽하게 좋은 질문 (이 코드 패턴은 실제로 의미가 있습니까), 매우 유용합니다. 질문에 +1하고 투표를 마친 모든 사람에게 눈살을 찌푸립니다.
MarkJ

9
Lucerno가 옳습니다.이 코딩 패턴은 괄호를 피하려고 시도함으로써 유도됩니다. Lisp에 노출 된 후에는 치료할 수 없습니다.
Hans Passant

13
최적화 된 코드 : (f as T).SomeMethod();)
MSalters

답변:


252

당신의 이해는 사실입니다. 그것은 나에게 미세 최적화하려고하는 것 같습니다. 유형이 확실하면 일반 캐스트를 사용해야합니다. 더 현명한 예외를 생성하는 것 외에도 빠르게 실패합니다. 유형에 대한 당신의 가정에 대해있는 거 잘못이 경우, 프로그램이 즉시 실패하고 당신은을 위해 장애 것이 아니라 즉시 대기의 원인을 볼 수 있습니다 NullReferenceException또는 ArgumentNullException미래에 또는 논리적 오류 언젠가. 일반적으로 어딘가에 검사가 as뒤 따르지 않는 표현식 null은 코드 냄새입니다.

반면, 캐스트에 대해 확신이없고 실패 할 것으로 예상 as되는 경우 try-catch블록으로 랩핑 된 일반 캐스트 대신 사용해야 합니다 . 또한 as유형 검사 후 캐스트에 사용하는 것이 좋습니다. 대신에:

if (x is SomeType)
   ((SomeType)x).SomeMethod();

이는 생성 isinst명령 에 대한 is키워드 및 castclass명령 캐스트 (효과적으로 두 번 캐스트를 수행)를 들어, 당신은 사용해야합니다 :

var v = x as SomeType;
if (v != null)
    v.SomeMethod();

isinst명령 만 생성합니다 . 이전의 방법은 경쟁 조건으로 인해 is검사가 성공하고 캐스트 라인에서 실패한 후 변수의 유형이 변경 될 수 있으므로 다중 스레드 응용 프로그램에 결함이있을 수 있습니다 . 후자의 방법은이 오류가 발생하지 않습니다.


다음 솔루션은 프로덕션 코드에 사용 하지 않는 것이 좋습니다 . C #에서 이러한 기본 구성을 정말로 싫어한다면 VB 또는 다른 언어로 전환하는 것을 고려할 수 있습니다.

캐스트 구문을 필사적으로 싫어하는 경우 캐스트를 모방하는 확장 메소드를 작성할 수 있습니다.

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

깔끔한 [?] 구문을 사용하십시오.

obj.To<SomeType>().SomeMethod()

5
경쟁 조건은 관련이 없다고 생각합니다. 이 문제가 발생하면 코드가 스레드로부터 안전하지 않으며 키워드 "as"를 사용하는 것보다 더 안정적인 방법으로 코드를 해결할 수 있습니다. 나머지 답변은 +1입니다.
RMorrisey

10
@RMorrisey : 적어도 하나의 예를 염두에두고 있습니다. cache다른 스레드가로 설정하여 무효화하려고 하는 객체가 있다고 가정하십시오 null. 잠금이없는 시나리오에서는 이런 종류의 일이 발생할 수 있습니다.
Mehrdad Afshari

10
+ 캐스트의 FxCop에서 "불필요하게 캐스팅하지 말라"경고를 게재 할 수있을만큼이다 : msdn.microsoft.com/en-us/library/ms182271.aspx 즉 구조를 피하기 위해 충분한 이유가 있어야한다.
David Schmitt

2
에 확장 메소드를 작성하지 않아야합니다 Object. 값 유형에 메소드를 사용하면 불필요하게 상자가 표시됩니다.
MgSam

2
@MgSam 분명히 이러한 유스 케이스는 To상속 계층 구조를 통해 변환 하기 때문에 메소드에 의미가 없습니다 . 물론 전체 아이디어는 진지한 것보다 이론적입니다.
Mehrdad Afshari


40

'as'를 사용하면 사용자 정의 전환이 적용되지 않지만 캐스트에서는 적절한 경우 전환을 사용합니다. 경우에 따라 중요한 차이가 될 수 있습니다.


5
이것은 기억해야합니다. 에릭 리퍼 트 ​​(Eric Lippert) : blogs.msdn.com/ericlippert/archive/2009/10/08/…
P Daddy

5
좋은 의견, P! 그러나 코드가이 차이에 의존한다면, 나중에 심야 디버깅 세션이 있다고 말할 것입니다.
TrueWill

36

나는 이것에 대해 조금 썼다 :

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

나는 당신의 요점을 이해합니다. 캐스트 운영자는 "이 객체는 해당 유형으로 변환 될 수 있다고 확신하며, 틀린 경우 예외를 기꺼이 감수 할 것입니다"라는 의사 소통에 동의합니다. "이 개체가 해당 형식으로 변환 될 수 있는지 잘 모르겠습니다. 틀린 경우 null을 지정하십시오."

그러나 미묘한 차이가 있습니다. (x는 T) .Whatever ()는 "x를 T로 변환 할 수있을뿐만 아니라 참조 또는 언 박싱 변환 만 포함하고, x가 null이 아니라는 것을 알고 있습니다." 그것은 ((T) x) .Whatever ()와 다른 정보를 전달하며 아마도 코드 작성자가 의도 한 것입니다.


1
마지막 문장에서 코드 작성자의 추측 적 방어에 동의하지 않습니다. ((T)x).Whatever() 또한x null이 아닌 의사 소통을 한다. 저자는 일반적으로 변환이 T참조 변환 또는 언 박싱 변환만으로 발생 하는지 , 또는 사용자 정의 또는 표현 변경 변환이 필요한지에 대해 신경 써야한다고 의심한다 . 결국을 정의 public static explicit operator Foo(Bar b){}하면와 Bar호환 되는 것으로 간주되는 것이 분명 Foo합니다. 이 변환을 피하고 싶지 않은 경우는 드 rare니다.
P Daddy

4
아마도 대부분 의 코드 작성자는 미묘한 차이를 만들지 않을 것입니다. 개인적으로는 그럴 수도 있지만 그럴 경우 그 효과에 의견을 추가합니다.
Eric Lippert

16

나는 종종 "오직"이 캐스팅보다 빠르다는 증거 로이 잘못된 기사에 대한 언급을 보았다 .

이 기사에서 가장 오해의 소지가있는 부분 중 하나는 측정 대상을 나타내지 않는 그래픽입니다. 나는 실패한 캐스트를 측정하고 있다고 생각합니다 (예외가 발생하지 않아 "as"가 훨씬 빠릅니다).

측정하는 데 시간이 걸리면 예상대로 주조가 더 빠름을 알 수 있습니다 한대로 캐스팅이 성공할 때 캐스팅이 "as"보다 는 있습니다.

이것이 캐스트 대신에 as 키워드를 "cargo cult"로 사용하는 한 가지 이유 일 수 있습니다.


2
링크 주셔서 감사합니다, 그것은 매우 흥미 롭습니다. 나는이 문서를 이해하는 방법에서, 그가 하지 않은 예외의 경우를 비교합니다. 그럼에도 불구하고이 기사는 .net 1.1 용으로 작성되었으며 주석은 .net 2.0에서 변경되었다고 지적합니다.
Heinzi

1
이 기사는 그가 예외가 아닌 경우를 비교하고 있음을 암시하지만 오래 전에 일부 테스트를 수행했으며 .NET 1.x에서도 자신의 주장 결과를 재현 할 수 없었습니다. 이 기사에서는 벤치 마크를 실행하는 데 사용되는 코드를 제공하지 않기 때문에 비교 대상을 말하는 것은 불가능합니다.
Joe

"화물 숭배"-완벽합니다. 자세한 정보는 "Cargo Cult Science Richard Feynman"을 확인하십시오.
밥 데니

11

직접 전송에는 as키워드 보다 괄호 쌍이 더 필요 합니다. 따라서 유형이 무엇인지 100 % 확신하는 경우에도 시각적 혼란을 줄입니다.

그러나 예외적 인 것에 동의했다. 그러나 적어도 나를 위해, 대부분의 사용은 나중에 as확인하기 위해 끓여서 null예외를 잡는 것보다 더 좋습니다.


8

"as"를 사용하는 시간의 99 %는 실제 객체 유형이 무엇인지 잘 모르겠습니다.

var x = obj as T;
if(x != null){
 //x was type T!
}

"is"를 사용하여 명시적인 캐스트 예외를 포착하거나 두 번 캐스트하고 싶지 않습니다.

//I don't like this
if(obj is T){
  var x = (T)obj; 
}

8
에 대한 올바른 사용 사례를 방금 설명했습니다 as. 다른 1 %는 무엇입니까?
P Daddy

오타로? =)이 정확한 코드 스 니펫을 사용하는 시간의 99 %를 의미 하지만 때로는 메소드 호출이나 다른 곳에서 "as"를 사용할 수 있습니다.
Max Galkin

D' oh, 그리고 그것은 두 번째 인기있는 대답보다 어떻게 덜 유용합니까 ???
Max Galkin

2
나는이 아웃 호출 동의 한 루벤스 파리 아스의 답변으로 가치 같습니다 - 사람들이 희망 여기에 올 것이다 이것이 유용한 예가 될 것이다
루벤 Bartelink

8

사람들이 외모를 좋아하기 때문에 읽기 쉽습니다.

C와 유사한 언어의 캐스팅 / 변환 연산자는 가독성이 매우 뛰어납니다. C #에서 다음 Javascript 구문 중 하나를 채택하면 더 좋습니다.

object o = 1;
int i = int(o);

또는 다음 to과 같은 캐스팅을 가진 연산자를 정의하십시오 as.

object o = 1;
int i = o to int;

언급 한 JavaScript 구문도 C ++에서 허용됩니다.
P Daddy

@PDaddy : 직접 100 % 호환되는 대체 구문은 아니며 그렇게 의도하지 않습니다 (연산자 X와 변환 생성자)
Ruben Bartelink

C ++ 구문을 사용하는 것이 좋습니다 dynamic_cast<>(). 당신은 못생긴 일을하고 있습니다.
Tom Hawtin-tackline

5

사람들 as은 예외로부터 안전하다고 느끼기 때문에 너무 좋아 합니다. 남자는 당신이 상자 안에 따뜻하고 건장한 느낌을주기를 원하기 때문에 멋진 보증을합니다. 밤에 베개 아래에 작은 상자를 두었다고하자 보증 요정이 내려 와서 1/4을 떠날 수도 있습니다.

직접 캐스트를 사용할 때 돌아 가기 주제에 ...의 존재 가능성 잘못된 캐스트 예외는. 따라서 사람들 은 스스로 스스로 예외를 던지지 as않기 때문에 모든 주조 요구에 담요 솔루션으로 적용 as합니다. 그러나 그에 대한 재미있는 점은 당신이 준 예입니다(x as T).SomeMethod(); 에서 null 참조 예외에 대해 잘못된 캐스트 예외를 거래하고 있다는 것입니다. 예외를 볼 때 실제 문제를 난독 화시키는 것은 무엇입니까?

나는 일반적으로 as너무 많이 사용하지 않습니다 . 나에게 is테스트를 선호합니다. 더 읽기 쉽고 캐스트를 시도하고 null을 확인하는 것이 더 합리적입니다.


2
"Is test"- "is"를 선호하고 캐스트는 물론 "as"보다 느리고, "IDictionary.ContainsKey"처럼 인덱서를 사용한 역 참조가 "IDictionary.TryGetValue보다 느리다" "). 그러나 더 읽기 쉬운 것으로 판단되면 그 차이는 거의 없습니다.
Joe

중간 부분에서 중요한 말은 사람들 as이 안전하다고 느끼기 때문에 담요 솔루션으로 적용하는 방법 입니다.
Bob

5

이것은 나의 최고 동료 중 하나 여야 합니다.

Stroustrup의 D & E 및 / 또는 내가 지금 찾을 수없는 블로그 게시물 tohttps://stackoverflow.com/users/73070/johannes-rossel (즉, 동일한 구문 as이지만DirectCast 의미론 의미 ).

이것이 구현되지 않은 이유는 캐스트가 통증을 유발하고 추악 해져 사용하지 못하기 때문입니다.

'영리한'프로그래머 (종종 저술가 (Juval Lowy IIRC)) as가 이런 방식 으로 학대함으로써이 문제를 해결하는 것이 유감입니다 (C ++은 as아마도 이런 이유로을 제공하지 않습니다 ).

심지어 VB는 당신이를 선택하는 힘있는 균일 한 구문을 가지고 더 일관성이 TryCastDirectCast하고 당신의 마음을 확인을 !


+1. 아마도 구문이 아니라 DirectCast 동작을 의미했을 것입니다 .
Heinzi

@Heinzi : +1에 대한 Ta. 좋은 지적. smartarse로 결정하고 semantics대신 사용 : P
Ruben Bartelink

C #이 C, C ++ 또는 Java와 호환성이 없다고 가정하면, 나는 그 언어에서 빌린 것들 중 일부를 엿 본다는 것을 알았습니다. "이것은 X라는 것을 알고 있습니다.", "이것은 X가 아니라는 것을 알고 있습니다." "하지만 어쨌든 X를 줘." 에 적합 할 수있는 정확한 값을 나타내지 않았지만 yield -1을 갖는 것이 단순히 추한 double-to-int경우 캐스트에 유용하다는 double것을 알 수있었습니다 . Int32(int)-1.5
supercat

@supercat Yep, 그러나 우리 모두가 알듯이 언어 디자인은 쉽지 않습니다. C # nullables와 관련된 트레이드 오프를 살펴보십시오. 유일하게 알려진 해독제는 깊이있는 에디션에서 C #을 정기적으로 읽는 것입니다. 고맙게도 요즘 F #의 뉘앙스를 이해하는 데 더 관심이 있으며 많은 문제에 대해 훨씬 제정신입니다.
Ruben Bartelink

@RubenBartelink : 나는 꽤 nullable 형식을 해결하기로했다 문제를 정확한 분명한 아니지만, 나는을 가지고 더 좋은했을 것이다 대부분의 경우에 생각 MaybeValid<T>이 개 공공 필드 IsValidValue는 적합한을보고 같이 함께 할 수있는 코드입니다. 그것은 예를 들어 허용했을 것 MaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }입니다. 이렇게하면 적어도 두 개의 복사 작업을 절약 Nullable<T>할 수있을뿐만 아니라 클래스뿐만 아니라 모든 유형의 값을 사용할 수도 T있습니다.
supercat

2

나는 믿고 as키워드가 좀 더 우아한 찾고 버전으로 간주 할 수있는 dynamic_castC ++에서.


C #에서 스트레이트 캐스트 dynamic_cast는 C ++에서 더 낫습니다.
P Daddy

C #의 스트레이트 캐스트는 C ++의 static_cast와 더 동등하다고 생각합니다.
Andrew Garrison

2
@Ruben Bartelink : 포인터와 함께 null 만 반환합니다. 가능하면 사용해야하는 참조와 함께을 던집니다 std::bad_cast.
P Daddy

1
@Andrew Garrison : static_cast런타임 유형 검사를 수행하지 않습니다. C #에는 이와 유사한 캐스트가 없습니다.
P Daddy

안타깝게도 포인터에 캐스트를 사용 했으므로 참조에 캐스트를 사용할 수 있다는 것을 몰랐지만 P 아빠는 절대적으로 정확합니다!
Andrew Garrison

1

기술적 인 이유는 없지만 읽기 쉽고 직관적이기 때문에 더 인기가있을 것입니다. (단지 질문에 대답하는 것이 더 나아지는 것은 아닙니다)


1

"as"를 사용하는 한 가지 이유 :

T t = obj as T;
 //some other thread changes obj to another type...
if (t != null) action(t); //still works

(잘못된 코드) 대신 :

if (obj is T)
{
     //bang, some other thread changes obj to another type...
     action((T)obj); //InvalidCastException
}

2
이 추악한 경쟁 조건을 가지고 있다면 더 큰 문제가 생길 수 있습니다 (그러나 다른 사람들과 함께 갈 수있는 좋은 샘플에 동의하십시오 +1
Ruben Bartelink

이 오류는 영속적이므로 -1입니다. 다른 스레드가 obj 유형을 변경할 수 있으면 여전히 문제가 있습니다. t가 T에 대한 포인터로 사용될 것이지만 더 이상 T가 아닌 메모리를 가리 키므로 "// 여전히 작동한다"라는 주장은 매우 어리 석습니다. 다른 스레드가 action (t)이 진행되는 동안 obj.
Stephen C. Steel

5
@Stephen C. Steel : 혼란스러워하는 것 같습니다. 유형을 obj변경하면 obj변수 자체를 변경하여 다른 객체에 대한 참조를 보유 할 수 있습니다. 원래에 의해 참조 된 객체가있는 메모리의 내용은 변경하지 않습니다 obj. 이 원래 객체는 변경되지 않은 상태로 유지되며 t변수는 여전히 이에 대한 참조를 보유합니다.
P Daddy


1
@ P Daddy-당신이 맞다고 생각하고 틀 렸습니다 .obj가 T 객체에서 T2 객체로 리바운드 된 경우 t는 여전히 이전 T 객체를 가리키고 있습니다. t는 여전히 이전 객체를 참조하므로 가비지 수집 될 수 없으므로 이전 T 객체는 계속 유효합니다. 내 경쟁 조건 탐지기 회로는 dynamic_cast를 사용하는 유사한 코드가 잠재적 인 문제가 될 수있는 C ++에서 훈련되었습니다.
Stephen C. Steel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.