Andreas Morgenstern
Andreas Morgenstern

Reputation: 299

F# Interactive: How to import type from System.Reflection.Assembly class

For a scripting engine I try to integrate C# into F# Interactive so that I can declare C#-classes inside of F# Interactive. I've managed to compile C#-code into a System.Reflection.Assembly object using Microsoft CSharp Code Provider (see below if you are interested in it). So assume that script is a System.Reflection.Assembly. Using

script.GetTypes()

I can get the type information for all types declared in the script, e.g. if my script is declared in the following C#-code :

    let CS_code="""
      namespace ScriptNS
          {

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

script.GetTypes will contain type information for the ScriptNS.Test class. What I'd like to achieve is to use this class like any other class in F# Interactive, e.g.

let t=new ScriptNS.Test()

Hence, my question: can I somehow import the Class to the FSI AppDomain?

Thanks for your help,

Andreas

P.S. One possibility I found is to use the #r directive. A working solution would be:

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()

However, I'm not happy with the additional dll.

Upvotes: 0

Views: 494

Answers (1)

John Palmer
John Palmer

Reputation: 25516

This started as a comment, but got a bit long:

So this is what #r does:

        | 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

From this, I think the interesting function is this one:

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 }

but it looks like all these options use functions, making this a little bit of a dead end. As a result, I am not quite sure if what you want is possible.

Upvotes: 1

Related Questions