Reputation: 299
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
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