Ссылки на сборку F #, вызывающие проблемы сборки?

У нас есть сборка F # (AssemblyOne), которая ссылается на другую сборку F # (AssemblyTwo) в одном решении Visual Studio 2012. AssemblyTwo имеет ссылку на С# DLL (MyCSharpLib).

Функция, определенная в AssemblyOne, вызывает функцию, определенную в AssemblyTwo:

namespace AssemblyOne

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

Функция, вызываемая в AssemblyTwo, вызывает другую функцию (FetchActualResult()) в той же самой сборке, которая принимает параметр типа MyCSharpType, который принадлежит ссылочной С# 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'.

Кажется, что тип, открытый как параметр из MyCSharpLib в сигнатуре функции FetchActualResult() в AssemblyTwo, вызывает ошибку. AssemblyOne теперь требуется ссылка на MyCSharpLib, хотя AssemblyOne напрямую не использует ничего из MyCSharpLib. Если мы удалим параметр из сигнатуры функции, решение будет построено без ошибок.

Мы дополнительно исследовали эту проблему, реплицируя код в следующих случаях использования ('- > ' указывает ссылку на сборку):

  • F # AssemblyOne → F # AssemblyTwoMyCSharpLib (С# DLL) (не создается)
  • F # AssemblyOne → F # AssemblyTwoMyFSharpLib (F # DLL) (не создается)
  • F # AssemblyOne → F # AssemblyTwo → С# AssemblyThree (сборка в том же решении) (не создается)
  • F # AssemblyOne → F # AssemblyTwo → F # AssemblyThree (сборка в том же решении) (строит)

Можно ли объяснить это поведение?

Ответ 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 одна конфигурация процесса компиляции или версия компилятора может попытаться "коснуться" MyModuleB и, таким образом, попытаться достичь типа параметров без ссылок, а другие могут просто не касаться MyModuleB. Это зависит.

Таким образом, проблема кажется мне не в процессе компиляции, а в том, что вы раскрываете использование типа, для которого вы не ссылаетесь на его сборку.

Ответ 2

Теперь я решил аналогичный случай. Попробуйте это.

В конце MyModuleC добавьте эту строку:

let fetchResult = FetchResult

И затем в MyModuleA вызовите fetchResult вместо FetchResult. Конечно, с аргументом.

Да, я знаю, это звучит глупо, но попробуйте. Я считаю, что это нарушит нежелательную зависимость.

Если вы используете AssemblyTwo из С# как есть, у вас, вероятно, не будет этой проблемы. Он возникает, когда вы потребляете AssemblyTwo от F #, поэтому я задаюсь вопросом, есть ли проблема с компилятором F #, или, возможно, это связано с каррированием, которое выходит за рамки меня. Во всяком случае, я бы хотел, чтобы компилятор F # был умнее. Может быть, кто-то должен подать вопрос, если это уже сделано.