2016-05-04 6 views
4

Я работаю над стандартным классом данных нашего программного обеспечения. Вот минимальный пример текущего состояния:Доступ к динамическому массиву с типом перечисления

TDataOne = (Width, Length, Height); 
    TDataTwo = (Diameter, Weight); 

    TDatStruct = class; 

    TDatSubStructOne = class(TDatStruct) 
    public 
     myData : array[TDataOne] of double; 
     procedure writeToFile(AFileName : string); 
    end; 
    TDatSubStructTwo = class(TDatStruct) 
    public 
     myData : array[TDataTwo] of double; 
     procedure writeToFile(AFileName : string); 
    end; 

Для того, чтобы уменьшить избыточность кода, я хотел бы представить себе структуру данных с общей функцией письма в TDatStruct. Проблемы (по крайней мере для меня, как любителя) являются:

  1. mydata имеет переменный размер в зависимости от substruct и баз на различных типах перечислений
  2. Я все еще хочу, чтобы получить доступ к mydata с перечисляемого типа в подразделам структур
  3. Я не хочу, чтобы передать данные суб структуры в качестве параметров к общей функции записи из-за числа различных массивов в реальном коде

Единственная идея (пренебрегая точка 3) у меня есть, чтобы пройти данные массива суб структура к общей процедуре записи как открытый параметр массива, как:

writeToFile(AFileName : string; writeData : array of double); 

Есть ли способ объединить динамический массив с перечислимым типом или виртуального перечисленномом массивом? Или я полностью ошибаюсь?

+0

Всегда используйте 'const' в своих определениях параметров, если это вообще возможно. – Johan

+0

Возможно, вы также хотите прочитать файлы? Тогда вам нужно будет определить, какой класс следует за двойными в файле, нет? –

+0

@Johan: Спасибо, я буду помнить об этом! – SiD

ответ

4

MYDATA имеет переменный размер в зависимости от substruct и баз на различных типах перечислений

Delphi имеет встроенные функции Low и High
Вам не нужно знать размер массива.

Я все еще хочу, чтобы получить доступ к MyData с перечисляемого типа в суб структурах

Используйте succ и pred собственные методы, чтобы пройти через перечисления.

Я не хочу, чтобы передать данные вспомогательной структуры в качестве параметра к общей функции записи из числа различных массивов в реальном коде

Все ваши массивы выглядят одинаково для меня. .. Они просто array of double, хотя и с различным количеством элементов.

Вы можете реализовать writeToFile так:

procedure writeArrayToFile(const AFileName : string; const writeData : array of double); 
var 
    i: integer; 
begin 
    for i:= low(writeData) to High(writeData) do begin 
    WriteADoubleToFile(AFilename, writeData[i]); 
    end; {for i} 
end; 

Обратите внимание на использование const, если вы не используете этот массив будет скопирован в стек, вызывая огромные задержки с большими массивами.

Если массив является членом данных вашего класса, то вы можете просто написать код так:

procedure TDatStruct.writeToFile(const AFileName : string); 
begin 
    WriteArrayToFile(FileName, GetMyData^); 
end; 

Обратите внимание, что, поскольку родительский класс TDatStruct фактически не имеют каких-либо данных внутри, вы» Вам нужно будет написать виртуальную функцию, которая получит эти данные.

type 
    TMyArray = array[0..0] of double; 
    PMyArray = ^TMyArray; 

..... 
TDatStruct = class 
protected 
    function GetMyData: PMyArray; virtual; abstract; 
public 
    procedure WriteToFile(const Filename: string); //no need for virtual 
end; 

open array parameter из WriteArrayToFile будет решить проблему для вас.

Обратите внимание, что параметры массива в writeToFile являются «параметром открытого массива», это будет принимать как статические, так и динамические массивы.

Если вы используете различные типы массивов (double/string и т. Д.).
Затем используйте общий метод.

Есть ли способ объединить динамический массив с перечисленным типом или виртуальным перечисляемым массивом? Или я полностью ошибаюсь?

Динамический массив имеет переменное количество элементов.
Вы не можете проиндексировать его, используя перечисление напрямую.
Однако вы можете перевести перечисленное значение в целое число с помощью встроенной функции Ord.
Он также работает для перечислений с более чем 256 значениями. Документация неверна, при необходимости она возвращает байты, слова или кардиналы.

function FixedArrayToDynamicArray(const input: array of double): TArray<double>; 
begin 
    //translate a fixed array to a dynamic array. 
    SetLength(Result, High(input)); 
    Move(input[0], Result[0], SizeOf(Double) * High(input)); 
end; 

предварительно дженерики код становится:

type 
    TDoubleArray = array of double; 

function FixedArrayToDynamicArray(const input: array of double): TDoubleArray; 
... same from here on. 
+0

Я думаю, что функция GetMyData - именно то, что я ищу. У меня возникла проблема получить ссылку на переменную, которая определена только как перечислимый массив в потомке. Как только я пробовал это успешно с моим кодом, я помечаю как решенный! – SiD

+0

Когда я реализую функцию 'GetMyData' как' Результат: = @ myData', у меня проблема с тем, что 'High (writeData)' в функции 'writeArrayToFile' возвращает 0, и я могу получить доступ только к первому элементу массива. – SiD

+0

@ Сид, да, результат GetMyData имеет только один элемент. Хм, вам придется использовать 'FixedArrayToDynamicArray' или вернуть GetMyData количество элементов в параметре' out'. – Johan

0

Есть много разных подходов, это трудно сказать, что лучше всего в вашей ситуации.

Конечно, WriteToFile должны быть введены в TDatStruct в качестве реферата:

TDatStruct = class 
    public 
    procedure WriteToFile(AFileName: string); virtual; abstract; 

и потомки должны реализовать его по-разному:

TDatStructOne = class (TDatStruct) 
    ... 
    public 
    procedure WriteToFile(AFileName: string); override; 

таким образом, вы можете иметь объект класса TDatStruct, который может на самом деле быть TDatStructOne или TDatStructTwo, но код, который его использует, не нужно «знать», что он имеет в виду: базовый полиморфизм.

В таком случае у вас есть много свободы в отношении типов данных, которые вы используете для хранения информации в каждом классе, реализации могут отличаться. Самое простое было бы:

TDatStructOne = class (TDatStruct) 
    public 
    Width: Double; 
    Length: Double; 
    Height: Double; 
    procedure WriteToFile(AFileName: string); override; 
end; 

TDatStructTwo = class (TDatStruct) 
    public 
    Diameter: Double; 
    Weight: Double; 
    procedure WriteToFile(AFileName: string); override; 
end; 

и в WriteToFile каждый потомки делает свою работу.

Таким образом, ваши проблемы:

  1. вы будете иметь данные переменного размера, не обязательно массива двойников, все, что вы хотите (строки, целые наборы и т.д.)
  2. Вы получили удобный доступ ко всем этим полям, если у вас есть объекты класса TDatStructOne или TDatStructTwo (конкретные классы)
  3. Не существует обычной процедуры записи. Вместо этого у вас есть общий «интерфейс записи». Вы пишете obj.SaveToFile(FileName), и он будет сохранен соответствующим образом.
+0

Вы даже не рассмотрели проблему перечисления, кроме того, что ваш код не работает, потому что он не принимает массив в качестве параметра. – Johan

+0

@ Johan У меня есть сильное чувство, что OP возится с массивами только потому, что это единственный способ сохранить различные данные без всякой необходимости, но это будет работать некоторое время, но тогда будет TDatStructThree, в котором хранятся не только удвоения и это все разломится. Но, конечно, моя «телепатия» работает только в 50% случаев, может быть, здесь я ошибаюсь. –

+0

Я думаю, что массив на самом деле является правильным способом решения проблемы. Опрыскивание, теряющее двойники вокруг, не кажется правильным решением. И это не масштабируется. Использование массивов делает решение тривиальным. – Johan