如何使用默认构造函数创建记录类

发布于 2024-12-09 13:35:24 字数 1649 浏览 2 评论 0原文

结构有默认构造函数,就像我这样做一样,

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

我可以执行 new DeviceModel.tagONEDEV_FlowRec() 但它不适用于此:

let (<++|) device bytes size =
    let unmanagedPtr = Marshal.AllocHGlobal(size : int)
    Marshal.Copy( (bytes : byte array), 0, unmanagedPtr, size)
    Marshal.PtrToStructure(unmanagedPtr, (device : obj)) // Here
    Marshal.FreeHGlobal(unmanagedPtr)

我在这里需要一个类似的记录类

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

,或者

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

但这里没有默认构造函数和结构非常大到零手动初始化它们,那么我如何将此类与默认构造函数一起使用?

如果我找不到解决方案,我认为在 C# 甚至 VB.NET 上重新编码这部分对我来说会更快。听起来很像拐杖解决方案,但看起来我还无法使用 F# OOP 部分进行拨号。

另外:我不想输入的是:

               {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; }

<-这是我默认想要的,因为我有更大的结构,并且编写这样的构造函数是邪恶的。

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), 0, 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

ゝ杯具 2024-12-16 13:35:24

看来我找到了一个有效的技巧:

[<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

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
一身仙ぐ女味 2024-12-16 13:35:24

如果您出于某种原因确实想要初始化一条记录,正如约翰所说,有
“黑客解决方案”。

这是我的:(我同意如果你必须这样做,你可能做错了,但我向你保证我的确实对我所做的事情有帮助:)。

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

这里是你将如何使用它:

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

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

unsafeDefaultValuesFor<B>

结果将如下所示:
{字符串=空;整数=0; A = {字符串= null;整数=0; StringOption = null;};}

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;};}

颜漓半夏 2024-12-16 13:35:24

正如 jpalmer 试图解释必须使用参数初始化记录一样。我认为您应该尝试使用

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

此处查看: MSDN Docs Explicit字段

是的,我知道有更多的代码行和额外的“struct”、“end”和一些“val”,但这真的这么难吗?如果您的类型使用了 1000 个属性,无论如何您都应该重新考虑您的代码。

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.

南巷近海 2024-12-16 13:35:24

在 F# 中,您可以使用隐式语法放置默认构造函数,就像

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

我认为您使用 record 的方式有点奇怪,在 F# 中,记录类似于具有命名成员的元组 - http://msdn.microsoft.com/en-us/library/dd233184.aspx

编辑

所以问题基本上是你是否可以有一个将 a 归零的构造函数记录类型 - 回答否。在这种情况下,我认为最好的答案是从记录切换到结构/类,以便您可以获得所需的行为。

我想到的一种黑客解决方案是使用 System.Reflection.Assembly.CreateInstance ,它有时可以帮助解决此类问题,但编译器不提供默认构造函数,因此它不起作用

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

别在捏我脸啦 2024-12-16 13:35:24

使用 CLIMutable 声明带有隐式默认构造函数的记录。

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

为了防止代码漂移,我推荐使用以下样式来代替上面的样式。

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

“代码漂移”是指诸如属性之类的内容在没有人注意到的情况下偏离其最初预期的代码元素的趋势。使用第二种样式,由于编辑时的粗心,属性更不可能偏离声明。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文