F#程序集引用导致构建问题?

33
我们有一个F#程序集(AssemblyOne),它在单个Visual Studio 2012解决方案中引用另一个F#程序集(AssemblyTwo)。AssemblyTwo引用了一个C# DLL(MyCSharpLib)。
在AssemblyOne中定义的函数调用了在AssemblyTwo中定义的函数:
namespace AssemblyOne

[<RequireQualifiedAccess>]
module MyModuleA =
    let FetchResult id =
        let result = AssemblyTwo.MyModuleC.FetchResult id
        result

AssemblyTwo中调用的函数调用了同一程序集中的另一个函数 (FetchActualResult()),该函数接受一个类型为MyCSharpType参数,该类型属于所引用的C# DLL (MyCSharpLib):

namespace AssemblyTwo

[<RequireQualifiedAccess>]
module MyModuleB  =
    let FetchActualResult(myCSharpType:MyCSharpLib.MyCSharpType, id:int)
        //return a result

[<RequireQualifiedAccess>]
module MyModuleC =
    let FetchResult id =
        let myCSharpType = new MyCSharpLib.MyCSharpType()
        MyModuleB.FetchActualResult(myCSharpType, id)

这个解决方案在Visual Studio中可以编译和构建;但是,当我们尝试使用MSBuild从命令行构建项目时,构建失败,并在msbuild.log中显示以下错误:

error FS0074: The type referenced through 'MyCSharpLib' is defined in an assembly that is not referenced. You must add a reference to assembly 'MyCSharpLib'.

MyCSharpLibAssemblyTwoFetchActualResult()函数签名中作为参数暴露出来所引起的错误,导致现在AssemblyOne需要引用MyCSharpLib,即使AssemblyOne并没有直接使用MyCSharpLib中的任何内容。如果我们从函数签名中删除此参数,则解决方案将无错误构建。

我们通过复制代码来进一步探索这个问题,具有以下用例(“->”表示程序集引用):

  • F# AssemblyOne -> F# AssemblyTwo -> MyCSharpLib(C# DLL)(无法构建)
  • F# AssemblyOne -> F# AssemblyTwo -> MyFSharpLib (F# DLL)(无法构建)
  • F# AssemblyOne -> F# AssemblyTwo -> C# AssemblyThree(在同一解决方案中的程序集)(无法构建)
  • F# AssemblyOne -> F# AssemblyTwo -> F# AssemblyThree(在同一解决方案中的程序集)(构建成功)

这种行为可以解释吗?


我不确定是什么原因导致了这个问题,但通常你可以检查以下几点:(1) 它们是否都引用了相同版本的 FSharp.Core.dll?(2) 它们是否都编译为相同的目标框架? - Tomas Petricek
(1) 它们都引用了相同版本的 FSharp.Core.dll - 版本为 4.3.0.0;(2) 所有程序集的目标框架均为 v4.5。 - daithimurf
1
你确定你正在使用与VS相同的MSBuild版本吗?你能在某个地方发布完整的MSBuild日志吗? - skolima
两者都使用ToolsVersion="4.0"。 - daithimurf
我本来期望这段代码 MyModuleA.FetchActualResult 实际上应该是 MyModuleB.FetchActualResult,但我可能还没有完全理解情况。 - DWright
显示剩余6条评论
2个回答

1
假设您的源代码中存在笔误,正如DWright所指出的那样,我会说,这个错误可能仅仅是由于您通过此代码定义了一个具有外部类型MyCsharpType参数的静态类MyModuleB所导致的。
以下是F#代码转换为IL的方式(从ILSpy翻译回C#):
...
public static class MyModuleB
{
    public static string FetchActualResult(MyCSharpType myCSharpType, int id)
    {
        return myCSharpType.Fetch(id);
    }
}

如果您不公开类型以使其静态可见,则可能不会出现错误。但是,这取决于编译器的实现。

我可以想象,在编译MyModuleA时,一种编译过程配置或编译器版本可能会尝试“触及”MyModuleB,从而尝试访问未引用的参数类型,而其他编译过程则可能不会触及MyModuleB。 这取决于情况。

因此,问题在于您公开了一个类型的使用,但没有引用它的程序集。


0

我刚刚用类似的方法解决了一个问题。你可以试试这个。

在 MyModuleC 的结尾添加这一行:

let fetchResult = FetchResult

然后,在MyModuleA中调用fetchResult而不是FetchResult。当然要带参数。

是的,我知道这听起来很傻,但请尝试一下。我相信它会打破不必要的依赖关系。

如果您直接从C#使用AssemblyTwo,可能不会遇到此问题。它只会在您从F#中使用AssemblyTwo时出现,因此我想知道是否存在F#编译器的问题,或者它与柯里化有关,这超出了我的理解范围。无论如何,我希望F#编译器更加智能化。除非已经完成,否则可能应该提交问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接