“隐式重载”可能吗?

时间:2012-01-11 23:36:36

标签: types f# arguments overloading implicit

我有以下示例:

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 个答案:

答案 0 :(得分:3)

我认为你写它的方式可能是最好的选择。

如果你不想做拳击,那么肯定需要重载write函数,因为序列化需要针对不同类型实现不同。在这种情况下使用inline成员也不起作用,因为inline函数只需要特定实例或静态方法 on 它获得的某些值,但不是具体超载。

避免一些重复的合理解决方案是仅定义一个重载函数(即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的所有三种类型(stringWrite,...)编写了包装器,那么您可以编写:

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 :(得分:1)

重构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的类型被注释以选择合适的过载。