F#Interactive:如何从System.Reflection.Assembly类导入类型

时间:2014-08-26 09:47:39

标签: c# reflection f# f#-interactive

对于脚本引擎,我尝试将C#集成到F#Interactive中,以便我可以在F#Interactive中声明C#类。我已经设法使用Microsoft CSharp Code Provider将C#-code编译成System.Reflection.Assembly对象(如果您对它感兴趣,请参阅下文)。 因此,假设脚本是System.Reflection.Assembly。 使用

script.GetTypes()

我可以获取脚本中声明的所有类型的类型信息,例如如果我的 脚本在以下C#代码中声明:

    let CS_code="""
      namespace ScriptNS
          {

            public class Test
            {
                public string AString()
                {
                    return "Test";
                }
            }
          }"""

script.GetTypes将包含ScriptNS.Test类的类型信息。 我想要实现的是像F#Interactive中的任何其他类一样使用此类,例如

let t=new ScriptNS.Test()

因此,我的问题是:我可以以某种方式将类导入FSI AppDomain吗?

感谢您的帮助,

安德烈亚斯

P.S。我发现的一种可能性是使用#r指令。一个有效的解决方案是:

open System.Reflection
open System.CodeDom.Compiler

// Create a code provider
let csProvider=new Microsoft.CSharp.CSharpCodeProvider()
// Setup compile options
let options=new CompilerParameters()
options.GenerateExecutable<-false
options.GenerateInMemory<-false
let tempfile="c:\Temp\foo.dll"
options.OutputAssembly<-tempfile
let result=csProvider.CompileAssemblyFromSource(options,CS_code)
let script=result.CompiledAssembly
#r "c:\Temp\foo.dll"
// use the type 
let t=new ScriptNS.Test()

但是,我对附加的dll不满意。

1 个答案:

答案 0 :(得分:1)

这是作为评论开始的,但有点长:

这就是#r所做的:

        | IHash (ParsedHashDirective(("reference" | "r"),[path],m),_) -> 
            let resolutions,istate = fsiDynamicCompiler.EvalRequireReference istate m path 
            resolutions |> List.iter (fun ar -> 
                let format = 
                    if tcConfig.shadowCopyReferences then
                        let resolvedPath = ar.resolvedPath.ToUpperInvariant()
                        let fileTime = File.GetLastWriteTimeUtc(resolvedPath)
                        match referencedAssemblies.TryGetValue(resolvedPath) with
                        | false, _ -> 
                            referencedAssemblies.Add(resolvedPath, fileTime)
                            FSIstrings.SR.fsiDidAHashr(ar.resolvedPath)
                        | true, time when time <> fileTime ->
                            FSIstrings.SR.fsiDidAHashrWithStaleWarning(ar.resolvedPath)
                        | _ ->
                            FSIstrings.SR.fsiDidAHashr(ar.resolvedPath)
                    else
                        FSIstrings.SR.fsiDidAHashrWithLockWarning(ar.resolvedPath)
                fsiConsoleOutput.uprintnfnn "%s" format)
            istate,Completed

从这一点来看,我认为有趣的功能就是这个:

member __.EvalRequireReference istate m path = 
    if Path.IsInvalidPath(path) then
        error(Error(FSIstrings.SR.fsiInvalidAssembly(path),m))
    // Check the file can be resolved before calling requireDLLReference 
    let resolutions = tcImports.ResolveAssemblyReference(AssemblyReference(m,path),ResolveAssemblyReferenceMode.ReportErrors)
    tcConfigB.AddReferencedAssemblyByPath(m,path)
    let tcState = istate.tcState 
    let tcEnv,(_dllinfos,ccuinfos) = 
        try
            RequireDLL tcImports tcState.TcEnvFromImpls m path 
        with e ->
            tcConfigB.RemoveReferencedAssemblyByPath(m,path)
            reraise()
    let optEnv = List.fold (AddExternalCcuToOpimizationEnv tcGlobals) istate.optEnv ccuinfos
    istate.ilxGenerator.AddExternalCcus (ccuinfos |> List.map (fun ccuinfo -> ccuinfo.FSharpViewOfMetadata)) 
    resolutions,
    { istate with tcState = tcState.NextStateAfterIncrementalFragment(tcEnv); optEnv = optEnv }

但看起来所有这些选项都使用了函数,这使得它有点死路一条。因此,我不太确定你想要的是什么。