2012-01-18 2 views
19

나는 컴파일러의 동작을 설명하고자하는 상황이 있습니다. 우리가 Foo 클래스의 서명을 변경 한 경우봉인 된 키워드는 캐스트에 대한 컴파일러의 의견에 영향을줍니다.

static class FooGetterGetter 
{ 
    public static IFoo<T> Get<T>() 
    { 
     return (IFoo<T>)new FooGetter(); 
    } 
} 

sealed 키워드 추가 :

interface IFoo<T> 
{ 
    T Get(); 
} 

class FooGetter : IFoo<int> 
{ 
    public int Get() 
    { 
     return 42; 
    } 
} 

다음 컴파일 및 실행 : 약간의 코드를 감안할 때

sealed class FooGetter : IFoo<int> // etc 

을 그렇다면 다음 줄에 컴파일러 오류가 발생합니다.

의3210

:

'MyNamespace.IFoo <T>'에 유형 'MyNamespace.FooGetter을'변환 할 수 없습니다

사람이 sealed 키워드와 관련하여 여기에 무슨 일이 일어나고 있는지 설명 할 수 있습니까? 이것은 비주얼 스튜디오에서 .NET 4 프로젝트에 대해 C# 4입니다 2010 년

업데이트 :가 흥미롭게 내가 sealed를 적용 할 때 다음과 같은 코드를 수정 이유를 궁금해 할 때 동작의 일부에 발견 :

return (IFoo<T>)(IFoo<int>)new FooGetter(); 

업데이트 : 요청 T의 유형이 콘크리트 종류에 따라 사용 T의 유형과 동일 할 때 단지 설명을위한, 모든 것이 잘 실행됩니다. 종류가 다른 경우, 캐스트는 같은과 런타임에 실패

'MyNamespace.IFoo`1 [선택 System.Int32]'

를 입력 유형 'MyNamespace.StringFoo'의 개체를 캐스팅 할 수 없습니다

위의 예에서 StringFoo : IFoo<string>이고 발신자가 int을 요청합니다.

+2

내가 대답이 없어,하지만 난 그것을'IFoo 이''FooGetter' 구현 반면 열린 제네릭 형식이 사실 함께 할 수있는 뭔가가 상상 'IFoo '는 닫힌 제네릭 유형입니다. –

+1

참고 사항 : 나는 질문을 게시하기 전에 정의 된 동작을 받았는지 확인했습니다. 자신을 바보로 만들고 싶지 않았습니다. 왜 그것이 허용되는지, 컴파일러가 무슨 일이 일어나는 지 보증 할 수없는 것을 볼 수 있습니다. 그것이 성공의 기회를 가진다는 것을 알고 있습니다. 그러나 어떤 이유로 인해 봉인 된 키워드가 존재할 때 성공할 가능성은 사라집니다. 파생 될 수 없기 때문에 T와 일치 할 수 없다고 가정합니다. –

+0

+1 흥미 롭습니다 :) – leppie

답변

8

FooGetter은 일반적으로 IFoo<T>을 구현하는 대신 IFoo<int>을 명시 적으로 구현 한 것이므로 그것이 봉인 되었기 때문에 컴파일러는 IFoo<T>Tint이 아닌 경우이를 캐스팅 할 방법이 없다는 것을 알고 있습니다. 봉인되지 않은 경우 컴파일러에서 Tint이 아닌 경우 런타임에 예외를 컴파일하고 예외를 throw합니다.

당신은 이외로 사용하려고 경우 int 당신은 예외가 (예를 들어 FooGetterGetter.Get<double>();) :

유형의 개체를 캐스팅 할 수 없습니다 'MyNamespace.FooGetter' '를 입력 할 MyNamespace.IFoo`1 [System.Double] '. 컴파일러는 하지이 밀봉되지 않은 버전에 오류가 발생하지 왜 모르겠어요 무엇

이다.서브 클래스 FooGetter은 어떻게 new FooGetter()을 부여하면 IFoo<{something_other_than_int}>을 구현할 수 있습니까?

업데이트 :

Dan BryantAndras Zoltan 방법이 있습니다 당 생성자에서 파생 클래스를 반환 (또는 아마도 더 정확하게 에 대한 컴파일러 특성을 분석하여 다른 유형을 반환). 클래스가 봉인되지 않으면 기술적으로 가능합니다.

+4

+1하지만 마지막 질문에 대한 답변 - 글쎄 - 인터페이스입니다 - 그래서 인터페이스의 다른 인스턴스를 추가 할 수 있습니까? 컴파일러는'new'를 보지 못합니다.'FooGetter' 타입의 표현식 만 볼 수 있습니다.'sealed'가 아닌 경우에는 파생 된 타입이 될 수 있습니다. –

+0

@Stanley 나는 당신의 요점을 안다. 귀하의 설명은 또한 봉인 된 키워드를 두 번 캐스팅하는 데 적용됩니다. 캐스트가 두 번있는 상황에서, 구체적인 타입이'int'를 사용한다면'IFoo '로 형변환 할 수 있다는 것을 알고 있습니다. 컴파일러가 할 수있는 것과 같은 이유로'IFoo '에서'IFoo ' 'FooGetter' ~'IFoo ' (봉인되지 않았 음) - 어떤 이유로 런타임에 오류가 발생할 수 있습니다. –

+3

Andras의 요점은 "SecondFoo : FooGetter, IFoo "입니다. 왜 그것이 기본 클래스가 올바른 인터페이스를 구현할 수 있고 런타임에 에러를 남기지 않는다고 가정하지 않으면 파생 된 타입을 보장하지 않는'new'를 무시하는 이유에 대해 궁금합니다. –

4

개봉 어떤 파생 된 클래스의 클래스 IFoo<T> 구현할 수 : FooGetter 밀봉 것으로 표시되면

class MyClass : FooGetter, IFoo<double> { } 

는 컴파일러가 존재할 수 IFoo<int> 이외 IFoo<T>의 추가 구현을 할 수없는 것을 알고 FooGetter.

이것은 좋은 동작이므로 런타임이 아닌 컴파일 타임에 문제가 발생하는 것을 방지 할 수 있습니다.

(IFoo<T>)(IFoo<int>)new FooGetter();이 작동하는 이유는 이제 밀봉 된 클래스를 어떤 것으로도 구현 될 수있는 IFoo<int>으로 표시하기 때문입니다. 실수로 컴파일되지는 않았지만 의도적으로 컴파일러 검사를 무시하기 때문에 좋은 해결 방법입니다.

1

기존 답변에 추가하기 만하면됩니다. 실제로 사용 된 제네릭과는 아무런 관련이 없습니다.

interface ISomething 
{ 
} 

class OtherThing 
{ 
} 

그런 다음 (방법) 안에 말 :

OtherThing ot = XXX; 
ISomething st = (ISomething)ot; 

작품 잘

이 간단한 예를 생각해 보자. 컴파일러는 OtherThingISomething 일 수 있는지 알지 못하므로 성공할 것이라고 판단 할 때 믿습니다. 그러나 OtherThing을 (즉 sealed class OtherThing { } 또는 struct OtherThing { }) 인 으로 변경하면 더 이상 전송할 수 없습니다. 컴파일러는 이것이 잘 돌아갈 수 없다는 것을 알고 있습니다. (은/null이되지만, C# 규칙은 밀폐형에서 밀폐형으로 구현되지 않은 인터페이스로의 캐스트를 여전히 허용하지 않습니다).

질문 업데이트 : (IFoo<T>)(IFoo<int>)new FooGetter() 작성은 (IFoo<T>)(object)new FooGetter() 작성과 크게 다르지 않습니다. 사이의 변환을 원하는 유형의 두 가지 모두에 대한 확실한/아마도 조상 인 중간 유형을 거치면 모든 제네릭을 "허용"할 수 있습니다 (제네릭 또는 비공유). 그것은이 패턴과 매우 유사 :

void MyMethod<T>(T t) // no "where" constraints on T 
{ 
    if (typeof(T) = typeof(GreatType)) 
    { 
    var tConverted = (GreatType)(object)t; 
    // ... use tConverted here 
    } 
    // ... other stuff 
} 
관련 문제