2015-01-12 13 views
6

Mam ogólną klasę, która używa Typ ogólny typu Enum. Mój problem, w jaki sposób używać GetEnumName na wystąpienie tego typu?Jak wywołać metodę GetEnumName z ogólnym typem wyliczeniowym?

Utworzyłem małą klasę demo do zilustrowania problemu:

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; 

używam Delphi XE. Czy można to zrobić? A jeśli tak, to w jaki sposób?

+0

chodzi o edycję, sugeruję, że w kodzie produkcyjnym to Byłoby czystsze, aby dodać kilka metod klasy statycznej, takich jak 'ToOrdinal',' FromOrdinal' z mojej odpowiedzi. Pozwoli Ci to na napisanie metody 'ToString' jako' Result: = GetEnumName (System.TypeInfo (TKey), ToOrdinal (Key)); 'i tak unikniesz zanieczyszczania tej metody wyższego poziomu za pomocą lokalnych zmiennych i gnarly wywołań' Move' . –

+1

Nie sądzę, że * należy użyć polecenia Przenieś *. Zamierzonym sposobem byłoby użycie kodu związanego z TValue, np. 'TValue.Od (Key) .AsInteger', powiedziałbym. – TLama

+1

@TLama Które powoduje 'EInvalidCast' –

Odpowiedz

5

Osobiście zrobię to, dzwoniąc pod numer Move. Mam następujący typ:

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 metoda robi to, co trzeba, i jestem pewien, że będziesz w stanie dostosować go do swojej klasy.

Jeśli nie podoba ci się używanie Move w ten sposób, możesz użyć TValue.

TValue.From<TKey>(Key).AsOrdinal 

I @TLama wskazuje, że można uniknąć wywoływania GetEnumName w ogóle za pomocą

TValue.From<TKey>(Key).ToString 

Na pierwszy rzut oka, za pomocą TValue wydaje się być bardziej zgodne z etosem generycznych i RTTI . Wywołanie do Move opiera się na szczegółowych szczegółach implementacji typów wyliczeniowych. Jednak bardzo interesujące jest przejrzenie debuggera i obserwacja, ile kodu jest zaangażowane w wykonanie TValue.From<TKey>(Key).AsOrdinal. Samo to wystarczy, aby wahać się, aby polecić korzystanie z TValue.

Jeszcze innym sposobem osiągnięcia tego celu jest wykorzystanie TRttiEnumerationType:

TRttiEnumerationType.GetName<TKey>(Key) 

Realizacja ta jest o wiele bardziej wydajny niż przy użyciu TValue.ToString, będąc trochę więcej niż wywołanie GetEnumName.

+0

Twoja wersja z przeniesieniem nie jest kompatybilna z systemami big-endian – Atys

+0

Delphi jest zawsze małym endianem –

+0

Ok, myślałem, że urządzenia mobilne to BE. Najwyraźniej potrafią jedno i drugie. – Atys

1

To jest zaktualizowana wersja af mojej klasy, z sugerowaną zmianą. Dzięki Dawida i 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; 

i trochę przykładzie testowym:

skopiuj kod do onCreate od From:

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

Możesz przekształcić w "podobny sposób" (http://pastebin.com/SKqNgQG1) także Twój konstruktor ;-) – TLama

Powiązane problemy