2009-04-26 10 views
20

클래스 참조를 사용하여 객체의 인스턴스를 만들려면 어떻게해야합니까? 생성자가 실행되는지 확인 하시겠습니까? 이 코드 예제에서 클래스 참조에서 Delphi 객체를 만들고 생성자 실행을 보장하려면 어떻게해야합니까?

은,이 문장은 TMyClass의 생성자가 호출되지 않습니다 :

type 
    TMyClass = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 

constructor TMyClass.Create; 
begin 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    Clazz: TClass; 
    Instance: TObject; 
begin 
    Clazz := TMyClass; 
    Instance := Clazz.Create; 
end; 

답변

23

사용이 :

type 
    TMyClass = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 
    TMyClassClass = class of TMyClass; // <- add this definition 

constructor TMyClass.Create; 
begin 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    Clazz: TMyClassClass; // <- change TClass to TMyClassClass 
    Instance: TObject; 
begin 
    Clazz := TMyClass; // <- you can use TMyClass or any of its child classes. 
    Instance := Clazz.Create; // <- virtual constructor will be used 
end; 

다른 방법을 "TMyClass의 클래스"대신에 TMyClass에 타입 캐스트를 사용할 수 있습니다.

+1

좋아, 만약 내가 델파이로 일반 개체 팩토리를 구축하고 싶다면 변수에 TMyClass 클래스를 할당해야한다는 것을 알았다면 이것이 가능할 것 같습니다. – mjn

+1

특정 유형의 객체를 생성하려면 클래스 유형 정보가 필요합니다. 클래스 정보가 없으면이 유형의 객체를 생성 할 수 없습니다. 분명히;) – Alex

6

코드는 약간 수정 :

type 
    TMyObject = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 
    TMyClass = class of TMyObject; 

constructor TMyObject.Create; 
begin 
    inherited Create; 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    C: TMyClass; 
    Instance: TObject; 
begin 
    C := TMyObject; 
    Instance := C.Create; 
end; 
10

AfterConstruction 무시가 옵션인지 확인하십시오.

+1

TObject의 가상 메서드이므로 매우 새로운 합성 루트 클래스를 추가 할 필요가 없습니다. 이 아이디어 +1. – mjn

+0

참으로 매우 유용합니다! –

18

알렉산더의 해결책은 훌륭한 것이지만 특정 상황에서는 충분하지 않습니다. TClassFactory 클래스를 설정하고자 할 때, TClass 참조는 런타임 중에 저장 될 수 있고 나중에 검색되는 임의의 수의 인스턴스를 저장할 수 있습니다.

이러한 클래스 팩토리는 보유한 클래스의 실제 유형에 대해 전혀 알지 못하므로 해당 메타 클래스에 캐스트 할 수 없습니다. 이러한 경우 올바른 생성자를 호출하려면 다음 접근 방식이 효과적입니다.

먼저 간단한 데모 클래스가 필요합니다 (공용 필드는 신경 쓰지 말고 데모 목적으로 만 사용하십시오).

interface 

uses 
    RTTI; 

type 
    THuman = class(TObject) 
    public 
    Name: string; 
    Age: Integer; 

    constructor Create(); virtual; 
    end; 

implementation 

constructor THuman.Create(); 
begin 
    Name:= 'John Doe'; 
    Age:= -1; 
end; 

이제 우리는 순수 RTTI에 의해 올바른 생성자 호출 유형 THuman의 객체를 인스턴스화합니다.

procedure CreateInstance(); 
var 
    someclass: TClass; 
    c: TRttiContext; 
    t: TRttiType; 
    v: TValue; 
    human1, human2, human3: THuman; 
begin 
    someclass:= THuman; 

    // Invoke RTTI 
    c:= TRttiContext.Create; 
    t:= c.GetType(someclass); 

    // Variant 1a - instantiates a THuman object but calls constructor of TObject 
    human1:= t.AsInstance.MetaclassType.Create; 

    // Variant 1b - same result as 1a 
    human2:= THuman(someclass.Create); 

    // Variant 2 - works fine 
    v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]); 
    human3:= THuman(v.AsObject); 

    // free RttiContext record (see text below) and the rest 
    c.Free; 

    human1.Destroy; 
    human2.Destroy; 
    human3.Destroy; 
end; 

당신은 객체 "human1"와 "human2는"우리가 원하는 것을하지 않은, 즉, 이름 = ''연령별 = 0, 0으로 초기화 된 것을 발견 할 것이다. 대신 human3 개체는 THuman의 생성자에서 제공되는 기본값을 보유합니다.

그러나이 방법을 사용하려면 클래스에 매개 변수가없는 생성자 메서드가 있어야합니다. 위의 모든 것은 나에 의해 잉태되지 않았지만 훌륭하게 그리고 자세히 설명되어 있습니다 (예 : c.Free 부분). Rob Love's Tech Corner.

0

기본 클래스에서 추상 메서드를 만들고 생성자에서이 메서드를 호출하고 클래스 참조에서 만든 경우 실행할 자식 클래스를 재정의 할 수 있습니다.

+0

이것은 정확히 질문이 요구하는 것이 아닙니다. – bummi

+0

나는이 문제에 직면했다. 어떤 목록 매니저의 생성자에서 클래스 참조를 전달할 때, 어떤 종류의 자식이리스트를 채웠는지를 지정하고,리스트가 자식을 만들었을 때 자식 생성자의 부모 기본 클래스 라고 불렀다 ... 아이들 아이템의 수호자는 ...나에게 유일한 해결책은 생성자가 매개 변수를 가져야하기 때문에 추상 메소드를 선언하고, 부모 생성자에서 호출하고, 하위 클래스에서 재정의했다는 것입니다. 이 해결 방법은 누군가가 우리의 동일한 문제에 직면하도록 도울 수 있습니다 ... 안부 인사! – alijunior