다이렉트 캐스팅 대 'as'연산자?


709

다음 코드를 고려하십시오.

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
}

세 가지 유형의 캐스팅의 차이점은 무엇입니까? 어느 것을 선호해야합니까?


1
복제본은 아니지만 이전 질문 에서 성능에 대한 토론도 있습니다 .
Unsliced

8
넷째 : string s = Convert.ToString(o); 5 일 : string s = $"{o}"(또는 string.Format이전 C # 의 양식 과 동일 )
Earth Engine

답변:


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

예외 InvalidCastException이를 경우 o하지 않은 것입니다 string. 그렇지 않으면, 양수인 os, 경우에도이 o있다 null.

string s = o as string; // 2

를 할당 null하는 s경우 o없는 string경우, 또는 o이다 null. 따라서 값 유형과 함께 사용할 수 없습니다 (이 경우 연산자는 절대 리턴 할 수 없음 null). 그렇지 않으면에 할당 o합니다 s.

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

원인 NullReferenceException이 있는 경우 o입니다 null. 유형 이 무엇 이든 관계없이 o.ToString()로 돌아가는 모든 것을 할당 합니다 .so


대부분의 전환에 1을 사용하면 간단하고 간단합니다. 무언가가 올바른 유형이 아닌 경우 일반적으로 예외가 발생할 것으로 기대하기 때문에 거의 2를 사용하지 않는 경향이 있습니다. 오류 코드를 사용하는 잘못 설계된 라이브러리 (예 : 예외를 사용하는 대신 null = error를 반환)를 사용하여이 반환 null 유형의 기능에 대한 필요성 만 보았습니다.

3은 캐스트가 아니며 메소드 호출입니다. 문자열이 아닌 객체의 문자열 표현이 필요할 때 사용하십시오.


2
명시 적으로 정의 된 경우 값 유형에 '널'을 지정할 수 있습니다 (예 : int? 나는; 문자열 s = "5"; i = s로서 int; // i는 이제 5 s = null입니다. i = s로서 int; // 나는 이제 null이다
Anheledir

3
RE : Anheledir 실제로 첫 번째 호출 후에는 null이됩니다. 문자열 값을 얻으려면 명시 적 변환 함수를 사용해야합니다.
Guvante

45
RE : Sander 실제로 사용하는 또 다른 좋은 이유가 있습니다. 검사 코드를 단순화합니다 (null을 확인한 다음 null 및 올바른 유형을 확인하십시오) 이것은 사용자 지정 예외를 throw하는 데 많은 시간이 걸리기 때문에 유용합니다. 그러나 전화가 나쁘기 때문에 장님이라는 것은 매우 사실입니다.
Guvante

5
# 2는 입력 유형을 모르는 Equals 메소드와 같은 경우에 편리하지만 일반적으로 1이 선호됩니다. 그보다 선호되는 유형은 분명히 유형 시스템을 사용하여 하나만 기대할 때 하나의 유형으로 제한하는 것입니다. :)
Calum

6
# 2는 또한 특수 유형에 특정한 작업을 수행 할 수 있지만 그렇지 않은 작업을 수행하는 코드가있는 경우에도 유용합니다.
AnthonyWJones

349
  1. string s = (string)o;무언가 다른 것이 분명 해야 할 때 사용하십시오 .
  2. string s = o as string;다른 것이 있을 때 사용하십시오 .
  3. string s = o.ToString(); 그것이 무엇인지 상관하지 않지만 사용 가능한 문자열 표현을 사용하려는 경우에 사용하십시오.

1
이 답변은 잘 들리지만 정확하지 않을 수 있습니다.
j riv

1
나는 처음 두 개를 좋아하지만 세 번째 옵션에 "널이 아닌 것이 확실하다"고 덧붙입니다.
Uxonith

2
요즘 Elvis (?.)를 사용하여 신경 쓰지 않아도됩니다. obj? .ToString ()
Quibblesome

@Quibblesome-좋은 답변이지만 반박에 대해 생각하지 않아야했습니다! 말 그대로 언어가 15 년 넘게 사용되었다는 사실이 제 마음을 아프게합니다. 어제 우리가 수석 개발자들에게 C #으로 전환하도록 설득하려고 노력하면서 "에디"한 느낌이 들었습니다.
Griswald_911

1
@Quicksome nice answer : 1/2/3을 추가하면 OP로 스크롤 할 필요가 없으므로 짜증이 날 것입니다. 나는 투표에 따라 오래된 답변의 순위를 매길 것입니다!
왜 theqq

29

o문자열 인지 알고 있는지 여부 와 문자열로 수행하려는 작업 에 따라 다릅니다 . 귀하의 의견이 o실제로 문자열 이라는 것을 의미 하는 경우 스트레이트 (string)o캐스트를 선호합니다 . 실패하지 않을 것입니다.

스트레이트 캐스트를 사용하는 것의 가장 큰 장점은 실패했을 때 InvalidCastException 을 얻는다 는 것입니다.

으로 as하면 연산자, o문자열이 아닌, s설정되어 null당신이 확실하고 테스트하려는 경우 편리되는 s:

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

그러나 해당 테스트를 수행하지 않으면 s나중에 사용 하고 NullReferenceException이 발생합니다. 거의 모든 행이 변수를 역 참조하고 하나를 던질 수 있기 때문에 이러한 변수 는 일반적으로 발생하고 일단 야생에서 발생하면 추적하기가 훨씬 어렵습니다. 반면에, 값 유형 (기본 또는 DateTime 과 같은 구조체 )으로 캐스트하려는 경우 스트레이트 캐스트를 사용해야합니다 as.

문자열로 변환하는 특별한 경우 모든 객체 ToString에는가 있으므로 onull이 아닌 경우 세 번째 메소드는 괜찮을 수 있으며 ToString메소드가 원하는 것을 할 수 있다고 생각합니다 .


2
한 가지 참고 사항 -nullable 값 형식 as과 함께 사용할 수 있습니다. IE가 작동하지 않습니다,하지만 ... 것이다o as DateTimeo as DateTime?
존 깁

if (s is string)대신 사용 하지 않습니까?
BornToCode

1
@BornToCode는 저에게 개인적인 취향입니다. 당신이하고있는 일에 따라, 종종 ising 후 , 당신은 어쨌든 다시 캐스팅해야하기 때문에 is와 hard cast가 있습니다. 어떤 이유로 든 as및 null 검사가 나에게 더 좋았습니다.
블레어 콘래드

9

어떤 유형으로 캐스트 할 수 있는지 이미 알고 있다면 C 스타일 캐스트를 사용하십시오.

var o = (string) iKnowThisIsAString; 

C 스타일 캐스트에서만 명시 적 유형 강제 변환을 수행 할 수 있습니다.

이 원하는 형식이고 당신이 그것을 사용하는 경우이를 사용하려고하고 있는지 당신이 모르는 경우 등의 키워드 :

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

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

참고 모든 종류의 변환 연산자를 호출하지 않습니다. 객체가 null이 아니고 기본적으로 지정된 유형이 아닌 경우에만 null이 아닙니다.

문자열로 캐스트 할 수없는 경우에도 ToString ()을 사용하여 사람이 읽을 수있는 모든 문자열 표현을 가져옵니다.


3
타입 변환 연산자와 관련하여 흥미로운 점이 있습니다. 전환을 생성 한 유형이 몇 가지 있으므로 그 시점에주의해야합니다.
AnthonyWJones

7

as 키워드는 FindControl 메서드를 사용할 때 asp.net에서 좋습니다.

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

object, 직접 캐스트와 마찬가지로 유형이 지정된 변수를 조작 한 다음 캐스트해야합니다 .

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

큰 것은 아니지만 코드 라인과 변수 할당을 저장하고 더 읽기 쉽습니다.


6

'as'는 'is'를 기반으로하며, 이는 객체가 정책적으로 호환 가능한지 (기본적으로 캐스트 할 수있는 경우) 런타임에 확인하고 확인에 실패하면 null을 반환하는 키워드입니다.

이 두 가지는 동일합니다.

'as'사용 :

string s = o as string;

'is'사용하기 :

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

반대로 c 스타일 캐스트는 런타임에도 수행되지만 캐스트를 수행 할 수없는 경우 예외가 발생합니다.

중요한 사실을 추가하기 만하면됩니다.

'as'키워드는 참조 유형에서만 작동합니다. 당신은 할 수 없습니다 :

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

이 경우 캐스팅을 사용해야합니다.


내 실수를 지적 해 주셔서 감사합니다. 나는 대답을 편집했다. 어머 미안합니다.
Sergio Acosta

5

2는 파생 형식으로 캐스팅하는 데 유용합니다.

a 가 동물 이라고 가정 하십시오 .

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

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

최소한의 캐스트 먹이를 얻 습니다 .


2
@Chirs Moutray, 특히 라이브러리 인 경우 항상 가능하지는 않습니다.
감속

5

이 페이지에서 실행 된 실험에 따르면 http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(이 페이지는 때때로 "잘못된 리퍼러"오류가 표시되기 때문에 새로 고침하면됩니다)

결론적으로 "as"연산자는 일반적으로 캐스트보다 빠릅니다. 때로는 몇 배나 빠르며 간신히 빠릅니다.

나는 "아수"라는 말을 더 읽기 쉽다.

따라서 빠르고 빠르며 (예외는 발생하지 않음) 읽기 쉬울 수 있으므로 항상 "as"를 사용하는 것이 좋습니다.


4

직접 캐스트가 없으므로 "(string) o"는 InvalidCastException을 발생시킵니다.

"o as string"은 예외가 발생하지 않고 s가 널 참조가됩니다.

"o.ToString ()"은 모든 종류의 캐스트가 아니며, 객체에 의해 구현되어 .net의 모든 클래스에 의해 어떤 식 으로든 구현됩니다. 호출 된 클래스와 문자열을 리턴합니다.

문자열로 변환하기 위해 Convert.ToString (someType instanceOfThatType)도 있습니다. 여기서 someType은 유형 집합 중 하나이며 기본적으로 프레임 워크 기본 유형입니다.


3

무언가를 추가 할 수 있다면 주어진 모든 대답은 좋습니다 : 문자열의 메소드와 속성 (예 : ToLower)을 직접 사용하려면 쓸 수 없습니다 :

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

당신은 쓸 수 있습니다 :

((string)o).ToLower();

그러나 대신 쓸 수 있습니다.

(o as string).ToLower();

as옵션은 (내 생각에 적어도) 더 읽을 수 있습니다.


(o as string) .ToLower () 구문은 as 연산자의 목적을 무효화합니다. o를 문자열로 캐스트 할 수없는 경우 널 참조 예외가 발생합니다.
james

@ james-그러나 누가 as 연산자의 유일한 목적은 캐스트가 실패하면 예외를 던지는 것이라고 말했습니까? 당신이 경우 이 오는 문자열이고 단지 청소기 코드를 작성하려는 당신이 사용할 수있는 (o as string).ToLower()브래킷을 혼란 여러 대신.
BornToCode

as의 목적은 정반대입니다. 캐스트가 실패 할 때 예외를 발생시키지 않아야하며 null을 반환해야합니다. o가 null 값을 가진 문자열이라고 가정하면 어떻게 될까요? 힌트-ToLower 통화가 실패합니다.
제임스

@ james-맞습니다.하지만 null이 아니며 특정 객체의 메소드에 액세스 할 수 있도록 컴파일러를 캐스팅해야한다고 생각하는 경우는 어떻습니까?
BornToCode

1
그렇게 할 수는 있지만 발신자 또는 외부 시스템에 의존하여 가치가 null이 아닌 것을 원하지 않기 때문에 최선의 방법은 아닙니다. C # 6을 사용하는 경우 (o로 문자열)? ToLower ().
james

3
string s = o as string; // 2

이중 주조의 성능 저하를 피하기 때문에 선호됩니다.


안녕하세요 크리스,이 답변에 포함 된 링크는 이제 404입니다 ... 교체 할 제품이 있는지 잘 모르겠습니다.
Matt

3

둘이 개념적으로 다른 것 같습니다.

다이렉트 캐스팅

유형이 엄격하게 관련 될 필요는 없습니다. 그것은 모든 종류의 맛으로 나옵니다.

  • 사용자 지정 암시 적 / 명시 적 캐스팅 : 일반적으로 새 개체가 생성됩니다.
  • 값 유형 암시 적 : 정보 손실없이 복사합니다.
  • 값 종류 명시 적 : 복사 및 정보가 손실 될 수 있습니다.
  • IS-A 관계 : 변경 참조 유형. 그렇지 않으면 예외가 발생합니다.
  • 동일한 유형 : '캐스팅이 중복됩니다'.

객체가 다른 것으로 변환되는 것처럼 느껴집니다.

AS 연산자

유형은 직접적인 관계가 있습니다. 에서처럼 :

  • 참조 유형 : IS-A 관계 개체는 항상 동일하며 참조 변경 사항 만 있습니다.
  • 값 유형 : 복사 복싱 및 널 입력 가능 유형.

객체를 다른 방식으로 처리하는 것처럼 느껴집니다.

샘플 및 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
    }


0

잠재적으로 null 일 수있는 모든 유형의 문자열 표현을 얻으려고 할 때 아래 코드 줄을 선호합니다. 컴팩트하고 ToString ()을 호출하며 null을 올바르게 처리합니다. o가 null이면 s는 String.Empty를 포함합니다.

String s = String.Concat(o);

0

아무도 언급하지 않았으므로 키워드로 Java에 가장 가까운 instanceOf는 다음과 같습니다.

obj.GetType().IsInstanceOfType(otherObj)

0

string s = (string) o;앱의 논리적 컨텍스트 string에서 유일하게 유효한 유형 인 경우 직접 전송을 사용하십시오 . 이 접근 방식을 통해 Fail-fastInvalidCastException 원칙을 얻고 구현할 수 있습니다. 논리가 유효하지 않은 유형을 더 이상 전달하지 못하도록하거나 연산자를 사용하는 경우 NullReferenceException을 가져옵니다 .as

로직에 여러 유형의 캐스트가 필요한 경우 string s = o as string;이를 확인 null하거나 is연산자를 사용하십시오 .

새로운 멋진 기능이 캐스트를 단순화하기 위해 C # 7.0의 등장과 검사는이다 한 패턴 매칭 :

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

C #에서는 다음 두 가지 형식 변환 (캐스팅)이 지원됩니다.

|

(이력서

• 주어진 표현식에서 v의 정적 유형을 c로 변환

• v의 동적 유형이 c 또는 c의 하위 유형 인 경우에만 가능

• 그렇지 않은 경우 InvalidCastException이 발생합니다.

|

v로 C

• 치명적이지 않은 (c) v의 변형

따라서 주어진 표현식에서 v의 정적 유형을 c로 변환

• v의 동적 유형이 c가 아니거나 c의 하위 유형 인 경우 null을 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.