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

时间:2016-02-01 10:50:27

标签: c# visual-studio-2012 f# msbuild

我们有一个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()),该函数接受属于引用的C#DLL MyCSharpType的类型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'.

MyCSharpLib FetchActualResult()中的AssemblyTwo函数签名中显示的AssemblyOne参数类型会导致错误。 MyCSharpLib现在需要引用AssemblyOne,即使MyCSharpLib没有直接使用AssemblyOne中的任何内容。 如果我们从函数签名中删除参数,则解决方案构建时没有错误。

我们通过使用以下用例复制代码来进一步探索此问题(' - &gt;'表示程序集引用):

  • F#AssemblyTwo - &gt; F#MyCSharpLib - &gt; AssemblyOne(C#DLL)(不构建)
  • F#AssemblyTwo - &gt; F#MyFSharpLib - &gt; AssemblyOne(F#DLL)(不构建)
  • F#AssemblyTwo - &gt; F#AssemblyThree - &gt; C#AssemblyOne(在同一解决方案中汇编)(不构建)
  • F#AssemblyTwo - &gt; F#AssemblyThree - &gt; F#public static OkHttpClient getUnsafeOkHttpClient() { try { File mFolder = new File(Environment.getExternalStorageDirectory() + "/certificate"); if (!mFolder.exists()) { mFolder.mkdir(); } String fileName = "certificate1a.cer"; File file = new File(mFolder, fileName); FileInputStream fis = null; fis = new FileInputStream(file); CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate ca = cf.generateCertificate(fis); // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Install the all-trusting trust manager final SSLContext sslContext = SSLContext.getInstance("TSL"); sslContext.init(null, tmf.getTrustManagers(),null); // Create an ssl socket factory with our all-trusting manager final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setSslSocketFactory(sslSocketFactory); okHttpClient.interceptors().add(new APIRequestInterceptor()); okHttpClient.interceptors().add(new APIResponseInterceptor()); okHttpClient.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession sslSession) { HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); Log.e("hey", "inside this"); Log.e("HOST VERIFIER", hv.toString()); Log.e("HOST NAME", hostname); return hv.verify("aviatesoftware.in", sslSession); } }); return okHttpClient; } catch (Exception e) { Log.e("error while getting ","unsafeOkHttpClient "+e.toString()); } return null; } public static <S> S createService(Class<S> serviceClass) { Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(getUnsafeOkHttpClient()) .build(); return retrofit.create(serviceClass); } GetInterface obj = createService(GetTaxInterface.class); Call<Abc> call = obj.getAbc("555555555555"); call.enqueue(new Callback<Abc>() { @Override public void onResponse(Response<Abc> response, Retrofit retrofit) { Log.e("Asynchronous response", response.toString()); Abc temp = response.body(); Log.e("tax object", temp.toString()); Toast.makeText(getApplicationContext(),"Retrofit Asynchonus Simple try successful",Toast.LENGTH_SHORT).show(); } @Override public void onFailure(Throwable t) { Log.e("on Failure", t.toString()); Toast.makeText(getApplicationContext(),"Retrofit Asynchonus Simple try failed",Toast.LENGTH_SHORT).show(); } }); (在同一解决方案中组装)(构建)

可以解释这种行为吗?

2 个答案:

答案 0 :(得分:1)

假设DWright指出您的源中存在拼写错误,我说这个错误可能源于这样一个事实:通过此代码,您可以使用外部类型的公开方法参数定义静态类MyModuleB MyCsharpType。

这就是Fsharp代码转换为IL的方式(来自ILSpy - 重新转换为Csharp):

...
public static class MyModuleB
{
    public static string FetchActualResult(MyCSharpType myCSharpType, int id)
    {
        return myCSharpType.Fetch(id);
    }
}

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

我可以想象,在编译MyModuleA期间,编译过程或编译器版本的一个配置可以尝试触摸&#34; MyModuleB,因此尝试到达未引用的参数类型,其他可能只是不触及MyModuleB。这取决于。

所以问题似乎在于我不在编译过程中,但事实上,你暴露了一个你不会引用它的程序集的类型的用法。

答案 1 :(得分:0)

我刚才用这种方式解决了类似的问题。试试这个。

在MyModuleC结束时,添加以下行:

let fetchResult = FetchResult

然后,在MyModuleA中,调用fetchResult而不是FetchResult。当然有论据。

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

如果您按原样使用C#中的AssemblyTwo,您可能不会遇到此问题。当你从F#使用AssemblyTwo时它会浮出水面,所以我想知道F#编译器是否存在问题,或者它可能与curry有关,这超出了我的范围。无论如何,我希望F#编译器更智能。也许某人应该提出问题,除非它已经完成。