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.
精彩评论