2015-01-12 2 views
6

일반 열거 형을 사용하는 일반 클래스가 있습니다. 내 문제는 어떻게 그 유형의 인스턴스에서 GetEnumName을 사용합니까? 델파이 XE를 사용하고GetEnumName을 일반 열거 형으로 어떻게 호출 할 수 있습니까?

type 
    TEnumSettings<TKey: record > = class 
    private 
     Key: TKey; 
    public 
     constructor Create(aKey: TKey); 
     function ToString: string; override; 
    end; 

uses 
    TypInfo; 


{ TEnumSettings<TKey> } 

constructor TEnumSettings<TKey>.Create(aKey: TKey); 
begin 
    if PTypeInfo(System.TypeInfo(TKey)).Kind <> tkEnumeration then 
    Exception.Create(string(PTypeInfo(System.TypeInfo(TKey)).Name) + ' is not an Enumeration'); 
    Key := aKey; 
end; 

function TEnumSettings<TKey>.ToString: string; 
begin 
    Result := GetEnumName(System.TypeInfo(TKey), Integer(Key)) <== HERE I get a compile error: Invalid type cast 
end; 

:

나는 문제를 설명하기 위해 작은 데모 클래스를 만들었습니다. 그렇게 할 수 있습니까? 그리고 만약 그렇다면 어떻게?

+0

당신의 편집에 관한 것은, 내가 생산 코드에서 그 좋을 것 그것 내 대답에서'ToOrdinal','FromOrdinal'과 같은 정적 클래스 메소드를 추가하는 것이 더 깔끔합니다. 그들은 당신이'Result : = GetEnumName (System.TypeInfo (TKey), ToOrdinal (Key));'와 같이'ToString' 메소드를 작성할 수 있도록 허용 할 것이므로이 상위 메소드를 지역 변수로 오염시키지 말고'Move' . –

+1

이동 명령 *을 사용해야한다고 생각하지 않습니다. 의도 된 방법은 'TValue'관련 코드를 사용하는 것입니다. 'TValue. (Key) .AsInteger'에서 말하고 싶습니다. – TLama

+1

@TLama 그 결과'EInvalidCast'가 나타납니다. –

답변

5

개인적으로 저는 Move으로 전화를 걸어 이것을 할 것입니다. 나는 다음과 같은 유형이 있습니다

type 
    TEnumeration<T: record> = class 
    strict private 
    class function TypeInfo: PTypeInfo; inline; static; 
    class function TypeData: PTypeData; inline; static; 
    public 
    class function IsEnumeration: Boolean; static; 
    class function ToOrdinal(Enum: T): Integer; inline; static; 
    class function FromOrdinal(Value: Integer): T; inline; static; 
    class function MinValue: Integer; inline; static; 
    class function MaxValue: Integer; inline; static; 
    class function InRange(Value: Integer): Boolean; inline; static; 
    class function EnsureRange(Value: Integer): Integer; inline; static; 
    end; 

{ TEnumeration<T> } 

class function TEnumeration<T>.TypeInfo: PTypeInfo; 
begin 
    Result := System.TypeInfo(T); 
end; 

class function TEnumeration<T>.TypeData: PTypeData; 
begin 
    Result := TypInfo.GetTypeData(TypeInfo); 
end; 

class function TEnumeration<T>.IsEnumeration: Boolean; 
begin 
    Result := TypeInfo.Kind=tkEnumeration; 
end; 

class function TEnumeration<T>.ToOrdinal(Enum: T): Integer; 
begin 
    Assert(IsEnumeration); 
    Assert(SizeOf(Enum)<=SizeOf(Result)); 
    Result := 0; // needed when SizeOf(Enum) < SizeOf(Result) 
    Move(Enum, Result, SizeOf(Enum)); 
    Assert(InRange(Result)); 
end; 

class function TEnumeration<T>.FromOrdinal(Value: Integer): T; 
begin 
    Assert(IsEnumeration); 
    Assert(InRange(Value)); 
    Assert(SizeOf(Result)<=SizeOf(Value)); 
    Move(Value, Result, SizeOf(Result)); 
end; 

class function TEnumeration<T>.MinValue: Integer; 
begin 
    Assert(IsEnumeration); 
    Result := TypeData.MinValue; 
end; 

class function TEnumeration<T>.MaxValue: Integer; 
begin 
    Assert(IsEnumeration); 
    Result := TypeData.MaxValue; 
end; 

class function TEnumeration<T>.InRange(Value: Integer): Boolean; 
var 
    ptd: PTypeData; 
begin 
    Assert(IsEnumeration); 
    ptd := TypeData; 
    Result := Math.InRange(Value, ptd.MinValue, ptd.MaxValue); 
end; 

class function TEnumeration<T>.EnsureRange(Value: Integer): Integer; 
var 
    ptd: PTypeData; 
begin 
    Assert(IsEnumeration); 
    ptd := TypeData; 
    Result := Math.EnsureRange(Value, ptd.MinValue, ptd.MaxValue); 
end; 

ToOrdinal 방법은 당신이 필요하지, 나는 당신이 당신의 클래스에 적응 할 수 있습니다 확신합니다.

이런 식으로 Move을 사용하고 싶지 않다면 TValue을 사용할 수 있습니다.

TValue.From<TKey>(Key).AsOrdinal 

그리고 @TLama는 TValue 제네릭 및 RTTI의 기풍과 유지에 더 많은 것 같다 사용하여, 그것의 얼굴에

TValue.From<TKey>(Key).ToString 

를 사용하여 모든 GetEnumName를 호출 피할 수 있다고 지적 . Move에 대한 호출은 열거 형의 특정 구현 세부 정보를 사용합니다. 그러나 디버거를 단계별로 실행하고 꽤 많은 코드가 TValue.From<TKey>(Key).AsOrdinal과 관련되어 있는지 관찰하는 것은 매우 흥미 롭습니다. 그것만으로도 TValue을 사용하는 것이 좋습니다.

는 그러나 이것을 달성하는 또 다른 방법은 TRttiEnumerationType를 사용하는 것입니다 :이의

TRttiEnumerationType.GetName<TKey>(Key) 

구현은 훨씬 더 효율적으로, TValue.ToString를 사용 GetEnumName로 전화보다는 조금 더 많은 것보다입니다.

+0

당신의 버전은 빅 엔디안 시스템과 호환되지 않습니다 – Atys

+0

델파이는 항상 리틀 엔디안입니다 –

+0

좋아요, 모바일 장치는 BE라고 생각합니다. 분명히 그들은 둘 다 할 수 있습니다. – Atys

1

변경 사항이 제안 된 클래스의 업데이트 된 버전입니다.

복사에서을의 한 OnCreate에 코드 : 다윗과 TLama

uses 
    TypInfo, Rtti; 

type 
    TEnumSettings<TKey: record> = class 
    private 
    Key: TKey; 
    public 
    constructor Create(aKey: TKey); 
    function ToString: string; override; 
    end; 


{ TEnumSettings<TKey> } 

constructor TEnumSettings<TKey>.Create(aKey: TKey); 
begin 
    if PTypeInfo(System.TypeInfo(TKey)).Kind <> tkEnumeration then 
    raise Exception.Create(string(PTypeInfo(System.TypeInfo(TKey)).Name) + ' is not an Enumeration'); 
    Key := aKey; 
end; 

function TEnumSettings<TKey>.ToString: string; 
begin 
    Result := TValue.From<TKey>(Key).ToString; 
end; 

그리고 약간의 테스트 예제 덕분에

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    with TEnumSettings<boolean> .Create(True) do 
    try 
     Caption := ToString; 
    finally 
     Free; 
    end; 
end; 
+2

구조체를 ['비슷한 방법으로] (http://pastebin.com/SKqNgQG1)로 설정할 수도 있습니다 ;-) – TLama

관련 문제