Indexed property – problem z modyfikacją pola rekordu

Odpowiedz Nowy wątek
2019-04-05 17:06
0

Chcę sobie napisać rekord, który jednocześnie będzie jakby dynamiczną tablicą rekordów, obudowaną w funkcje i nie mogę sobie poradzić z uzyskaniem właściwego rezultatu.

type
  TCell = record
    Value: Integer;
  end;

  TList = record
  strict private
    ConCell: array of TCell;
    function GetCell(Index: Integer): TCell;
    property Cell[Index: Integer]: TCell read GetCell; default;
  public
    procedure SetSize(pSize: Integer);
    procedure Clear;
  end;

var
  List: TList

=====

function TList.GetCell;
begin
  Result:= ConCell[Index];
end;

Dzięki takiej konstrukcji mogę raz "zarządzać" całą listą i np. chcąc ją wyczyścić albo zmienić jej wielkość, robię tak:

List.Clear;
List.SetSize(4);

A potem chcąc uzyskać dostęp do poszczególnych komórek listy, robię tak:

JakasZmiena:= List[0].Value;

zamiast:

JakasZmienna:= List.ConCell[0].Value;

Teoretycznie niewiele, ale to eleganckie rozwiązanie :). Sęk w tym, że to działa tylko przy odczycie i nie wiem jak to zakodować tak, żeby pozwoliło mi także zapisywać poszczególne zmienne w komórkach. Właściwość PropCell jako metodę zapisu (write) łyknie tylko coś takiego (z uwagi na zgodność typów):

procedure SetCell(Index: Integer; pCell: TCell);

Ale to odpada, bo w ten sposób mogę tylko przypisywać całe komórki, np.:

List[0]:= JakasKomorka; //JakasKomorka to zmienna typu TCell

Na coś takiego już mi nie pozwoli:

List[0].Value:= 167;

Da się to jakoś obejść?

edytowany 12x, ostatnio: furious programming, 2019-04-09 16:18

Pozostało 580 znaków

2019-04-11 16:23
0

@Crow: wiesz w ogóle czym są zmienne klasowe? Raczej nie…

Miałeś skorzystać ze zwykłej klasy, w której byłoby prywatne pole i publiczna właściwość:

type
  TFace = class(TObject)
  private
    FValue: Integer;
  public
    property Value: Integer read FValue write FValue;
  end;

albo od biedy publiczne pole bez właściwości, jeśli jakakolwiek walidacja itp. nie jest potrzebna:

type
  TFace = class(TObject)
  public
    Value: Integer;
  end;

Takie pola są unikalne dla każdej instancji (obiektu), więc modyfikacja danych jednego obiektu nie spowoduje zmiany danych w innych obiektach tej samej klasy. Druga istotna rzecz to to, że nie ma dostępu do tych pól przez utworzeniem instancji klasy, a po jej utworzeniu można te dane modyfikować tylko za pomocą referencji. Czyli tak:

var
  Face: TFace;
begin
  Face := TFace.Create();
  Face.Value := 100; // zmiana wartości

Zmienne klasowe to zupełnie inna bajka. Te przynależą do klasy i są wspólne dla klasy oraz wszystkich jej instancji. Istotny jest właśnie ten fakt – zmienna należy do klasy, a jej wartość można modyfikować za pomocą referencji, czyli tak:

type
  TFace = class(TObject)
    class var Value: Integer;
  end;

var
  Face: TFace;
begin
  Face := TFace.Create();
  Face.Value := 100; // zmiana wartości

ale też za pomocą samego identyfikatora klasy (bez konieczności tworzenia instancji):

type
  TFace = class(TObject)
    class var Value: Integer;
  end;

begin
  TFace.Value := 100; // zmiana wartości

Twój kod działa prawidłowo, dlatego że zmienna klasowa jest jedna, bez względu na liczbę utworzonych referencji. Modyfikując jej wartość sprawiasz, że nowa wartość dostępna jest z poziomu dowolnego elementu listy i jest taka sama.


Podsumowując, nie używaj rzeczy, których przeznaczenia nie rozumiesz i nie kombinuj, jeśli nie potrzebujesz. A po drugie, zanim użyjesz nowego elementu lub konstrukcji, najpierw kuknij do dokumentacji i zapoznaj się z jego/jej specyfiką, bo programowanie randomowe to z reguły bardzo zły pomysł. ;)


edytowany 7x, ostatnio: furious programming, 2019-04-11 16:29

Pozostało 580 znaków

2019-04-11 17:03
0

Dzięki za odpowiedź. A co do kombinowania, to właśnie w taki sposób uczę się nowych rzeczy :) (jak kiedyś pisałem, nie jestem programistą tylko humanistą ;p). Testuję -> nie działa -> próbuję rozwiązać problem -> profit :). No ale to chyba raczej nie zadziała, bo nijak nie jestem w stanie uzyskać dostępu do zmiennej Value wewnątrz TFace poprzez Faces[Index].Value. Przy próbie korzystania z właściwości, ciągle wywala mi ten sam błąd E2064 Left side cannot be assigned to, niezależnie czy używam domyślnego TList, własnego TList, czy tablic. W Lazarusie to może działa, w Delphi niestety nie ;/.

edytowany 1x, ostatnio: Crow, 2019-04-11 17:03

Pozostało 580 znaków

2019-04-11 17:36

Użyj generycznego TObjectList:

type
  TFaces = TObjectList<TFace>;

Pozostało 580 znaków

2019-04-11 22:24
0

Tak, w przypadku klas kompilator się już nie pluje i można je normalnie nadpisywać, nawet bez użycia właściwości. Czyli to było problemem, wystarczyło zamienić rekord na klasę. Dzięki! :).

edytowany 1x, ostatnio: Crow, 2019-04-11 22:25

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0