Понижение "obj" к базовому типу массива без аннотации типа - PullRequest
0 голосов
/ 07 октября 2018

Я работаю с некоторыми данными, которые считываются из двоичного файла и могут быть одного из нескольких примитивных типов, например, int16, int32, single, double и т. Д.

Например, скажем, у меня есть 3файлы:

  • Файл A: содержит все int16
  • Файл B: содержит все int32
  • Файл C: содержит все отдельные

Заголовок каждого файла имеет код для типа данных, например, Файл A будет иметь поле заголовка dtype: 0, Файл B будет иметь поле заголовка dtype: 1, а Файл C будет иметь поле заголовка dtype: 2.

Файлы представляют собой собственный формат изображения - представьте себе растровое изображение, но где тело растрового изображения может быть int16, int32 или одиночным.

Учитывая, что я прочитал данные из двоичного файлафайл, я использую сопоставление с шаблоном для преобразования двоичных данных в тип, указанный в заголовке файла.

Скажем, у меня есть массив / буфер, содержащий n байтов, считанных из файлового потока:

let buffer: byte[] = … <-- read bytes into here
let container = new ResizeArray<obj>() //maintain same type in pattern match

let matchDatatype (dtype: int) = // Let's read File B
    match dtype with
    | 0 ->             
        let typeBuffer: int16[] = numBytesInFile/2 |> Array.zeroCreate
        while stream.Position < stream.Length do
            stream.Read(buffer, 0, numBytesInFile) |> ignore
            Buffer.BlockCopy(buffer, 0, typeBuffer, 0, numBytesInFile)
            typeBuffer |> Array.chunkBySize 8 |> container.Add
    | 1 -> // Reading int32 from File B
        let typeBuffer: int32[] = numBytesInFile/4 |> Array.zeroCreate
        while stream.Position < stream.Length do
            stream.Read(buffer, 0, numBytesInFile) |> ignore
            Buffer.BlockCopy(buffer, 0, typeBuffer, 0, numBytesInFile)
            typeBuffer |> Array.chunkBySize 8 |> container.Add
    | 2 -> ...
    ….

Так что, если я читаю из файла B, код данных заголовка говорит: «читать этиНапример, int32 ", происходят другие вещи, и у меня есть ResizeArray<obj>, который содержит массив массивов (например, int[][])

Чтобы получить нужный мне массив, я просто нарезал ResizeArray (container.[0]) и я получаю obj.

Мне нужно вернуть obj обратно в тип массива.Проблема в том, что, поскольку я читаю файлы нескольких возможных типов, у меня возникают проблемы с обобщением кода для работы со всеми различными файлами.Я знаю, что могу сделать container.[0] :?> int[][], если знаю, что файл будет иметь все целые числа, но я не знаю, во время разработки.

Я знаю, что не могу сохранить тип массива какпозвольте связывание из GetType (), что еще больше сбивает с толку, как я должен подходить к этому (например, container.[0] :?> container.[0].GetType() не работает).

Используя fsi, вот пример того, что я пытаюсь сделать, если я прочитализ файла B (int):

> let someArray = [|[|0;1;2|];|[3;4;5|]|];; <-- say I read this from File B
  val it : int [] [] = [|[|0; 1; 2|];[|3; 4; 5|]|]

> container.Add(someArray)
  val it: unit = ()

> let arrObj = container.[0]
  val it : obj = [|[|0; 1; 2|];[|3; 4; 5|]|]

> arrObj.GetType().FullName;;
  val it : string = "System.Int32[][]"

> arrObj :?> int[][] <-- I can't know this at design time
  val it : int [] [] = [|[|0; 1; 2|];[|3; 4; 5|]|]

Последний шаг - место возникновения проблемы.Возвращенный объект типа ясно показывает, что он знает, что массив не является объектом - что это на самом деле int[][].Как я могу программно / динамически выполнить это понижение, не говоря явно «понижение до int[][]?»Мне нужно, чтобы это работало и для одиночных [] [] и для int16 [] [] случаев.

Или мой подход неверен, когда речь идет о коде, который может гибко считывать данные разных типов?Моя единственная другая мысль - сделать чудовищную попытку поймать, но я чувствую, что это не очень идиоматично.

Моя предыдущая работа была сделана в MATLAB, так что это новая проблема для меня, так как я мог простовыведите строки и сгенерируйте нужный мне код.

edit : использование Buffer.BlockCopy вместо BitConverter

edit 2 : я вижу, F # может определитьвведите псевдонимы, используя type, где

[accessibility-modifier] type-abbreviation = type-name

Однако это не позволяет мне делать что-то вроде type ArrType = arrObj.GetType().Самое близкое, что я думаю о том, что мне нужно, это C-подобный typedef.

edit 3 : Я смотрел на то, что называется динамическим созданием экземпляров, используя Activator.CreateInstance() - это тот случай, когда можно использовать это?

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Я думаю, что то, что предлагает kaefer , было бы наиболее идиоматичным.Объявите container как

let container = new ResizeArray<ArrayTypes>()

, и теперь вы можете правильно включать каждый массив строго типизированным способом.Ваш matchDataType метод, вероятно, также может быть изменен на что-то более похожее на

let add sz c = 
    let typeBuffer = numBytesInFile/sz |> Array.zeroCreate
    while stream.Position < stream.Length do
        stream.Read(buffer, 0, numBytesInFile) |> ignore
        Buffer.BlockCopy(buffer, 0, typeBuffer, 0, numBytesInFile)
        typeBuffer |> Array.chunkBySize 8 |> c |>  container.Add

let matchDatatype (dtype: int) = // Let's read File B
    match dtype with
    | 0 ->             
       add 2 I16
    | 1 -> // Reading int32 from File B
       add 4 I32 

Затем, когда вы обрабатываете свой список, вы можете сделать что-то вроде:

for t in container do
    match t with
    | I16 arr -> // do something with arr: int16[][]
    | I32 arr -> // do something with arr:   int[][]
    | F32 arr -> // do something with arr:single[][]

Я думаю, что это, вероятно,самый чистый подход, учитывая то, как вы описали свою проблему.

Однако можно делать динамически, используя технику, называемую «отражение»:

type T() = 
    static member DoSomethingWithAnArray<'t>(arr:'t[][]) = arr.[0].[0]

let doSomethingWithAnArray (arr:obj) = 
    let meth = typeof<T>.GetMethod("DoSomethingWithAnArray")
    // for simplicity, I'm not actually checking that arr is an array of arrays
    // but you could use IsArray and GetArrayRank twice to be sure
    let elementType = arr.GetType().GetElementType().GetElementType()
    meth.MakeGenericMethod(elementType).Invoke(null, [|arr|])

doSomethingWithAnArray(box [|[|1.0|]|])
|> printfn "%A"

Здесь мы динамически вызываем T.DoSomethingWithAnArray, передавая объект и возвращая объект обратно, хотя этот метод имеет подпись 't[][] -> 't.

0 голосов
/ 07 октября 2018

Если вы не можете определить тип во время компиляции, выполните проверку типа во время выполнения, возможно, в сочетании с привязкой шаблона as к переменной.Поскольку мы получаем информацию о типах, было бы расточительно отбрасывать ее снова, поэтому мы храним ее в некоторой структуре данных.

Для кодирования типов сумм, в частности, любого из int16[][], int32[][] иfloat32[][], F # предлагает дискриминационный союз .

type ArrayTypes =
| I16 of int16[][]
| I32 of int32[][]
| F32 of float32[][]

let arrayTypes : obj -> _ = function
| :? (int16[][]) as i16 -> I16 i16
| :? (int32[][]) as i32 -> I32 i32
| :? (float32[][]) as f32 -> F32 f32
| _ -> invalidOp "Unknown Array Type"

arrayTypes <| box[|[|0s;1s;2s|];[|3s;4s;5s|]|]
// val it : ArrayTypes = I16 [|[|0s; 1s; 2s|]; [|3s; 4s; 5s|]|]
arrayTypes <| box[|[|0;1;2|];[|3;4;5|]|]
// val it : ArrayTypes = I32 [|[|0; 1; 2|]; [|3; 4; 5|]|]
arrayTypes <| box[|[|0.f;1.f;2.f|];[|3.f;4.f;5.f|]|]
// val it : ArrayTypes = F32 [|[|0.0f; 1.0f; 2.0f|]; [|3.0f; 4.0f; 5.0f|]|]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...