如何为 System.Type 创建任意类型?

Daw*_*aws 5 testing f# fscheck

我正在尝试使用 FsCheck 初始化我的模型实体。这些模型位于 C# 中,通常通过实体框架的私有设置器进行初始化。例如(人为的):

public class Model
{
    public string One { get; private set; } 
    public int Two { get; private set; } 
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个 FsCheck 生成器,它自动使用每个属性的注册生成器来生成模型。像这样的东西:

let modelGenerator = 
    gen {
        let incident = new Model()
        typeof<Model>.GetProperties()
            |> Array.filter (fun p -> p.CanWrite)
            |> Array.iter (fun p -> 
                let! newVal = Arb.generateType p.PropertyType // I wish I could do this
                p.SetValue(incident, newVal))

        return incident
    }
Run Code Online (Sandbox Code Playgroud)

这有两个问题:

  1. 不能在计算表达式let!之外使用gen
  2. Arb.generateType不存在,而且我找不到一种方法来做到这一点

是否可以创建一个生成器来自动在我的模型上设置私有字段?

scr*_*wtp 5

借助反射的力量,一切皆有可能(或在运行时抛出)。

module Arb = 
    open System.Reflection 

    // this is just a helper type to do reflection on. 
    type internal GenerateInvoker = 
        static member Invoke<'typ> () = 
            Arb.generate<'typ>
            |> Gen.map box

    // Invokes a generic method using a runtime type as a generic argument.
    let generateType (typ: Type) =
        typeof<GenerateInvoker>
            .GetMethod("Invoke", BindingFlags.Static ||| BindingFlags.NonPublic)
            .MakeGenericMethod([|typ|])
            .Invoke(null, [||]) :?> Gen<obj>

let modelGenerator = 
    gen {
        let incident = new Model()
        let props =  
            typeof<Model>.GetProperties()
            |> Array.filter (fun p -> p.CanWrite)

        // gen builder implements For, so you can do something like this. 
        for prop in props do 
            let! newVal = Arb.generateType prop.PropertyType
            prop.SetValue(incident, newVal)

        return incident
    }

Gen.sample 1 3 modelGenerator
Run Code Online (Sandbox Code Playgroud)