2010-12-07 1 views
11

는 다음 있다고 가정 :Delphi 인터페이스 상속 : 왜 조상 인터페이스의 멤버에 액세스 할 수 없습니까?

//Note the original example I posted didn't reproduce the problem so 
//I created an clean example 
    type 
    IParent = interface(IInterface) 
    ['{85A340FA-D5E5-4F37-ABDD-A75A7B3B494C}'] 
     procedure DoSomething; 
    end; 

    IChild = interface(IParent) 
    ['{15927C56-8CDA-4122-8ECB-920948027015}'] 
     procedure DoSomethingElse; 
    end; 

    TGrandParent = class(TInterfacedObject) 
    end; 

    TParent = class(TGrandParent) 
    end; 

    TChild = class(TParent, IChild) 
    private 
     FChildDelegate: IChild; 
    public 
     property ChildDelegate:IChild read FChildDelegate implements IChild; 
    end; 

    TChildDelegate = class(TInterfacedObject, IChild) 
    public 
     procedure DoSomething; 
     procedure DoSomethingElse; 
    end; 
나는 이것이 당신이 DoSomething를 호출 할 수 것이라고 생각하지만,이 경우 될 것 같지 않습니다

:

procedure CallDoSomething(Parent: TParent); 
begin 
    if Parent is TChild then 
    TChild(Parent).DoSomething; 
end; 

컴파일러는 분명 그 것을 IParent의 멤버가 구현되어 있지 않으면 클래스가 컴파일되지 않으므로 인터페이스 상속을 적용합니다. 그럼에도 불구하고 컴파일러는 클래스가 인스턴스화되어 사용될 때 IParent의 멤버를 확인할 수 없습니다.

내가 명시 적으로 TMyClass의 클래스 선언에 IParent을 포함하여이 문제를 해결할 수 있습니다

TMyClass = class(TInterfacedObject, IChild, IParent) 

신경 끄시 고,이 아무것도 해결되지 않습니다.

+1

FObject가 FChild 여야한다고 가정 할 수 있습니까? –

+0

@Lieven, 감사합니다. 그 중 하나를 놓쳤습니다. 원본 소스에서 복사하여 명확하게하기 위해 이름을 변경했습니다. –

+2

"property Object"를 "property Obj"로 변경 한 후 코드가 Delphi 2009에서 컴파일됩니다. – kludg

답변

8

문제는 인터페이스 선언 또는 클래스 구현에없는,하지만 소비자 코드 :

procedure CallDoSomething(Parent: TParent); 
begin 
    if Parent is TChild then 
    TChild(Parent).DoSomething; // << This is wrong 
end; 

TChild는 방법 " 해봐요"을 가지고 있지 않기 때문에 작동하지 않을 것입니다. TChild 는 직접 IChild 을 구현된다면 TChild이 방법을 구현하는 것이기 때문에, 다음이 정상적으로 가능할 것이다 직접 및 IChild 인터페이스의 일부로서.

참고하지만, TChild개인 범위에 해봐요를 구현하는 경우,이 인터페이스하지만 정상 범위 지정 규칙을 통해 계속 액세스 할 것이라고는 여전히 클래스/유니 외부에서 (그것을 호출 할 수 없다는 것을 의미한다) TChild 참조를 사용하십시오. 귀하의 경우에는

, 당신은 단순히 적절한 인터페이스를 얻어 다음 인터페이스를 통해 필요한 방법을 호출해야합니다, 당신이 결정하기 위해 클래스 타입 테스트를 사용하지만

if Parent is TChild then 
    (Parent as IChild).DoSomething; 

을 (추론) 존재 구현 세부 사항 (TChild 구현 : IChild)에 의존하는 인터페이스의 일부입니다. 난 당신이 대신 그 구현 세부 사항에서이 종속성을 분리, 직접 인터페이스 테스트를 사용해야 제안 :

var 
    parentAsChild: IChild; 

    begin 
    if Parent.GetInterface(IChild, parentAsChild) then 
     parentAsChild.DoSomething; 
    end; 
+1

이것은 실제 문제입니다. 귀하의 제안에 따라 호출 지점이 호출 된 클래스와 너무 밀접하게 연결되어 있고 인터페이스를 확장해야한다는 사실을 발견하게되었습니다. 누군가 다른 사람의 코드를 유지할 때 그러한 삶이 있습니다. –

0

편집 :이 질문에 대한 답은 원래 질문이 수정되기 전에 게시 되었기 때문에 더 이상 적합하지 않습니다.


이 델파이 2010에서 컴파일 : 그것은 상속 된 인터페이스를 지원한다는 구현하는 클래스를 선언하지 않는 경우

type 
    IParent = interface(IInterface) 
    function DoSomething: String; 
    end; 

    IChild = interface(IParent) 
    function DoSomethingElse: string; 
    end; 

    TMyClass = class(TInterfacedObject, IChild) 
    private 
    public 
    function DoSomething: String; 
    function DoSomethingElse: String; 
    end; 

// ... 

procedure Test; 
var 
    MyObject : IChild; 
begin 
    MyObject := TMyClass.Create; 
    MyObject.DoSomething; 
end; 
+0

문제는 * implements * 절에 있습니다. 당신이 쓴 것은 본질적으로 TChild 클래스 OP가 쓴 것과 같습니다. –

+0

내 답변은 편집하기 전에 원래 게시물과 관련이있었습니다.이 예제 코드에서 그는 변수를 IChild로 선언하고 DoSomething을 호출하려고하면 컴파일러 오류가 발생합니다. –

11

, 다음 클래스는 상속 된 인터페이스의 변수와 호환 할당되지 않습니다. 게시 한 코드 샘플은 잘 작동해야하지만 (IChild 인터페이스 사용), TMyClass 인스턴스에서 IParent 변수로 지정하려고하면 문제가 발생합니다.

이유는 COM 및 ActiveX가 구현에서 하위 인터페이스 (IChild)를 구현하지만 해당 인터페이스의 조상 (IParent)을 거부하기 때문입니다. 델파이 인터페이스는 COM 호환이 가능하기 때문에이게 이상한 유물입니다.

약 10 년 또는 12 년 전에이 기사를 쓴 적이 있지만, 볼랜드 블로그는 Embarcadero 서버로의 전환을 견디지 못했습니다.

이 동작을 변경하는 컴파일러 지시문이있을 수 있습니다. 기억하지 않습니다.

+0

@dthorpe - 그래서 나는 그 인터페이스 상속과 함께 자랐습니다 진술 문명을 줄이기 위해 설탕은 전적으로 정확하지 않습니다 설탕?! –

+0

이것은 아마도 문제 일 수 있습니다. 실제 클래스는 일부 인터페이스에 대한 기본 구현을 제공하는 클래스 계층 구조의 하위 클래스입니다. 'DoSomething'이 호출되기 전에 상위 클래스로 함수에 전달 된 다음 하위 클래스로 캐스팅됩니다. –

+0

@Lieven : COM이 접한 것은 "단지"통사론적인 설탕입니다. : P – dthorpe

-1

QueryInterface의 델파이 구현 표준까지 없습니다. The ways people mess up IUnknown::QueryInterface 블로그 항목에서 Raymond Chen은 일반적인 구현 실패를 열거합니다. 가장 주목할만한 세 번째 점은

기본 인터페이스에 응답하지 않음. 파생 인터페이스를 구현할 때 기본 인터페이스를 암시 적으로 구현하므로 도 해당 인터페이스에 응답하는 것을 잊지 마십시오.

IShellView *psv = some object; 
IOleView *pow; 
psv->QueryInterface(IID_IOleView, (void**)&pow); 

일부 개체는 잊어 버리고 E_NOINTERFACE로 QueryInterface가 실패합니다.

상속 된 인터페이스가 클래스 나 그 조상 중 하나에 명시 적으로 연결되어 있지 않으면 델파이가이를 찾지 못한다. 객체의 인터페이스 테이블과 상속 된 유형을 탐색하고 인터페이스 ID가 일치 하는지를 확인하기 만하면 기본 인터페이스를 확인하지 않습니다.

+0

이 분석은 잘못된 것입니다. Danny는 Delphi가 이것을 설계 할 때 MS 코드에서 다소 이상하게 작동하고 있다고 설명했습니다. QI의 구현은 실제로 여러 가지가 결합 된 것입니다. 지원하는 인터페이스를 지정하고 TInterfacedObject에 제공된 QI를 사용하여 구현합니다. 그 기능이 귀하의 QI 구현의 유일한 부분이라고 생각하지 마십시오. 지원을 선언 한 인터페이스에 의존합니다. 조상들을 열거하는 것이 당신 직업입니다. –

+0

@DavidHeffernan 나는 왜 그들이 IClassFactory2 버그를 해결했는지 이해하고 이해하지만 파생 된 인터페이스를 구현하는 클래스가 그 조상을 암시 적으로 구현한다는 개념을 깨뜨렸다. –

+0

그들은 QI를 구현하지 않습니다. 너는. 인터페이스에 의해 구현되도록 선언합니다. –

관련 문제