开发者

How to create record class with default constructor

structures got default constructors like if I do

type tagONEDEV_FlowRec =
    struct
        .......
    end

I can do new DeviceModel.tagONEDEV_FlowRec() but it doesn't work with this :

let (<++|) device bytes size =
    let unmanagedPtr = Marshal.AllocHGlobal(size : int)
    Marshal.Copy( (bytes : byte array), 开发者_如何学Python0, unmanagedPtr, size)
    Marshal.PtrToStructure(unmanagedPtr, (device : obj)) // Here
    Marshal.FreeHGlobal(unmanagedPtr)

I need a record class here alike

[<type:StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)>]
type tagONEDEV_FlowRec = { 
    mutable ....;}

or

type tagONEDEV_FlowRec =
    class
        .......
    end

but there is no default constructor here and structures is very big to zero init them manually, so how can I use such classes with default constructors ?

If I will not find the solution I think that will be faster for me to recode this part on C# or even on VB.NET. Sounds alike crutch-solution but looking like I can't dial with F# OOP part yet.

an addition: the thing I don't want to type is :

               {TimeRec     = 0; 
                Num         = 0us;
                FlagErr     = 0us;
                C6          = 0.0;
                C2H6        = 0.0;
                C3H8        = 0.0;
                CH4         = 0.0;
                CO2         = 0.0;
                iC4H10      = 0.0;
                iC5H12      = 0.0;
                neoC5H12    = 0.0;
                N2          = 0.0;
                nC5H12      = 0.0;
                O2          = 0.0;
                nC4H10      = 0.0;
                He          = 0.0;
                H2          = 0.0;
                H2O         = 0.0;
                id          = 0us; }

<- that is what I want to have by default, because I've got much lager structures then this and writing such constuctors is wicked.


Seems like I found a working trick :

[<type:StructLayout(LayoutKind.Sequential, Pack=1, CharSet=CharSet.Ansi)>]
type tagONEDEV_FlowRec =
    class
        [<DefaultValue>] val RecTime         : int;
        [<DefaultValue>] val FlowTime        : int;
        [<DefaultValue>] val AbsPress        : single;
        [<DefaultValue>] val T               : single;
        [<DefaultValue>] val DP_V_FlowRate   : single;
        [<DefaultValue>] val Volume          : double;
        [<DefaultValue>] val Energy          : double;
        [<DefaultValue>] val id              : UInt16;
        new() = {} 
    end


If you for some reason really do want to initialize a record, as John said, there are "hackish solution"s.

Here is mine: (I agree with if you have to do this, you probably are doing it wrong, but i assure you mine really helped for what i was doing :).

let rec unsafeDefaultValues (x:System.Type) = 
  if x.IsValueType then System.Activator.CreateInstance(x)
  else if (Microsoft.FSharp.Reflection.FSharpType.IsRecord x) then
    let cntr = x.GetConstructors() |> Array.pick Some
    let values = 
      cntr.GetParameters()
      |> Array.map (fun p -> unsafeDefaultValues p.ParameterType)
    cntr.Invoke(values)
  else if (Microsoft.FSharp.Reflection.FSharpType.IsTuple(x)) then
    let tupleValues = 
      Microsoft.FSharp.Reflection.FSharpType.GetTupleElements(x)
      |> Array.map (unsafeDefaultValues)
    Microsoft.FSharp.Reflection.FSharpValue.MakeTuple(tupleValues, x)
  else if (x.IsArray) then
    box (System.Array.CreateInstance(x.GetElementType(), 0))
  else
    null

let unsafeDefaultValuesFor<'a> = unsafeDefaultValues typeof<'a> :?> 'a

End here is how you would use it:

type A = {
  String : string
  Int : int
  StringOption : string Option
}

type B = {
  String : string
  Int : int
  A : A  
}

unsafeDefaultValuesFor<B>

And the result would look like this: {String = null; Int = 0; A = {String = null; Int = 0; StringOption = null;};}


as jpalmer tried to explain a record has to be initialized with the parameters. I think you should try to use

type MyStruct =
    struct
        val mutable myInt : int
        val mutable myString : string
    end

see here: MSDN Docs Explicit Fields

yeah I know there are more lines of code and an additional "struct", "end" and some "val"s but is this really this hard? If you are using 1000 properties on your type you should rethink your code anyhow.


In F# you can put a default constructor using implicit syntax like

type type(arg1,arg2) =
   let v1 = arg1
   let v2 = arg2
   ....

I think you are using record in a way which is a bit odd, in F# a record is similar to a tuple with named members - http://msdn.microsoft.com/en-us/library/dd233184.aspx

EDIT

So the question is basically can you have a constructor which zeroes a record type - answer no. In this case I think the best answer is to switch from a record to a struct / class so that you can get the behaviour you want.

One hackish solution that I thought of was to use System.Reflection.Assembly.CreateInstance which can sometimes help with things like this, but the compiler provides no default constructor so it doesn't work


Use CLIMutable to declare a record with an implicit default constructor.

[<CLIMutable>]
type MyRecord = { MyField: int }

In order to prevent code drift, I recommend the following style in favor of the above.

type [<CLIMutable>] MyRecord = { MyField: int }

"Code drift" is the tendency for stuff like attributes to drift away from the code element for which it was originally intended, without anyone noticing it. With the second style, it is more unlikely that the attribute will drift away from the declaration due to carelessness while editing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜