Ссылки на сборку 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 # AssemblyTwo
→ MyCSharpLib
(С# DLL) (не создается)
- F #
AssemblyOne
→ F # AssemblyTwo
→ MyFSharpLib
(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 # был умнее. Может быть, кто-то должен подать вопрос, если это уже сделано.