Возможна ли «неявная перегрузка»? - PullRequest
1 голос
/ 12 января 2012

У меня есть следующий пример:

type Stream (capacity) =
    let data = Array.zeroCreate capacity
    member private s.position = ref 0
    static member private encoder = new Text.UTF8Encoding()
    static member private write (x, o, a : byte[]) = for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256)
    static member private write (x, o, a : byte[]) = for i = 0 to 1 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256s)
    static member private write (x : string, o : int, a : byte[]) = Stream.encoder.GetBytes(x, 0, x.Length, a, o)
    static member format (x : int, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a
    static member format (x : int16, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a
    static member format (x : string, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a

Прежде всего, извините за ужасно грязный код, я только новичок в F #. Как вы можете видеть, три format перегрузки отличаются только для их типа аргумента, хотя их тело одинаково (хотя и вызывает различные перегрузки write). Можно ли как-то сократить функции форматирования до одной, может быть встроенной?

Я прошу прощения за то, что здесь я полностью упустил смысл, но я не смог найти много информации по этому вопросу.

Ответы [ 2 ]

3 голосов
/ 12 января 2012

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

Если вы не хотите заниматься боксом, то перегруженная функция write определенно необходима, потому что сериализация должна быть реализована по-разному для разных типов.Использование члена inline также не работает в этом случае, потому что функция inline может требовать только определенный экземпляр или статический метод для какого-либо значения, которое она получает, но не определенной перегрузки.

Разумное решение, которое позволяет избежать некоторого дублирования, состоит в том, чтобы определить только одну перегруженную функцию (т.е. write) и затем передать ее явно в качестве аргумента другим функциям, если они в этом нуждаются.Вы могли бы написать что-то вроде:

type Stream (capacity) = 
    let data = Array.zeroCreate capacity 
    member private s.position = ref 0 
    static member private encoder = new Text.UTF8Encoding() 
    static member Write (x, o, a : byte[]) = 
        for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256) 
    static member Write (x, o, a : byte[]) = 
        for i = 0 to 1 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256s) 
    static member Write (x : string, o : int, a : byte[]) = 
        Stream.encoder.GetBytes(x, 0, x.Length, a, o) |> ignore
    // Format takes a writer function 'f' as the first argument
    static member Format (x, s) f = let a = Array.create s 0uy in f(x, 0, a); a 

// When you call 'Format' with 'Stream.Write' as an argument, 
// the compiler chooses the right overload for you
Stream.Format (1s, 100) Stream.Write 
Stream.Format ("foo", 100) Stream.Write 

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

Относительно inline, вы можете использовать это, чтобы указать, чтотип аргумента должен реализовывать некоторый конкретный элемент (экземпляр или статический), но вы не можете сказать, что должна быть определенная перегрузка.Если вы написали оболочки для всех трех типов (int16, string, ...), которые имеют статический член Write, вы можете написать:

let inline format< ^T when ^T : 
    (static member Write : ^T * int * byte[] -> unit)> (value:^T) size =
  let a = Array.create size 0uy
  (^T : (static member Write : ^T * int * byte[] -> unit) (value, 0, a)) 
  a

... но это дажеболее сложное решение, которое также требует написания дополнительного кода при вызове format, поэтому я бы не стал использовать этот подход (но может быть полезно знать, что он существует).

1 голос
/ 12 января 2012

Рефакторинг format функций очевиден;Вы можете создать универсальную функцию форматирования и передавать в качестве параметров различные функции Stream.write.Во-первых, поместите универсальную функцию перед любым членом класса:

    static let formatGeneric writeFunc x s =
           let a = Array.create s 0uy         
           writeFunc(x, 0, a)         
           a

и используйте ее для создания экземпляров различных функций формата:

    static member format (x, s) = 
           formatGeneric (fun (x: int, i, a) -> Stream.write(x, i, a)) x s
    static member format (x, s) = 
           formatGeneric (fun (x: int16, i, a) -> Stream.write(x, i, a)) x s
    static member format (x, s) = 
           formatGeneric (fun (x: string, i, a) -> Stream.write(x, i, a)) x s

Обратите внимание, что Stream.write записано в полном объемеФорма и тип x помечены для выбора подходящей перегрузки.

...