同一命名空间下双模块DLL在C#中仅单模块可见的问题咨询
我有一个F#类库项目,项目定义如下:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Library</OutputType> <TargetFramework>net7.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Include="Program.fs" /> <Compile Include="Api.fs" /> </ItemGroup> <ItemGroup> <PackageReference Include="OfficeIMO.Word" Version="0.11.0" /> </ItemGroup> </Project>
Program.fs包含docx命名空间下的WordProcessingML模块,Api.fs包含同一命名空间下的Api模块。项目可正常编译发布为DLL,但在C#项目中引用该DLL时,仅能看到WordProcessingML模块,Api模块不可见。
我的问题:
- 如何操作才能让C#项目使用
Api模块? - 当前做法存在什么问题?(我猜测DLL仅编译了第一个模块,未包含后续模块)
另外,我的代码组织遵循Scott Wlaschin的一份代码结构指南,想知道这份指南是否已经过时。
Api.fs代码如下:
// 遵循上述指南编写 namespace docx module Api = type Run = docx.WordProcessingML.Run type DocumentPart = docx.WordProcessingML.DocumentPart let Save(document: docx.WordProcessingML.Document): string = if isNull (box document) then nullArg "document" docx.WordProcessingML.Save document let Paragraph(runs: Run seq): docx.WordProcessingML.Paragraph = if isNull (box runs) then nullArg "runs" List.ofSeq runs let Document(path: string, parts: DocumentPart seq): docx.WordProcessingML.Document = if isNull (box path) then nullArg "path" if isNull (box parts) then nullArg "parts" {path=path; parts=(List.ofSeq parts)} let IntoPart(paragraph: docx.WordProcessingML.Paragraph): Result<DocumentPart, string>= if isNull (box paragraph) then Error("Null Parameter") else Ok(docx.WordProcessingML.Par paragraph)
问题1:让C#项目使用Api模块的操作方法
F#模块及模块内的let绑定函数默认是内部可见性(internal),外部程序集无法访问。要让C#项目能使用Api模块,需要修改两处:
- 给
Api模块添加public修饰符,使其对外可见:
namespace docx // 添加public修饰符 public module Api = // 原有代码保持不变 type Run = docx.WordProcessingML.Run // ... 其余代码
- 给模块内需要被C#调用的函数添加公共可见性:
- F# 5.0及以上版本可直接用
public修饰符:public let Save(document: docx.WordProcessingML.Document): string = if isNull (box document) then nullArg "document" docx.WordProcessingML.Save document - 旧版本可使用
[<Public>]属性:[<Public>] let Save(document: docx.WordProcessingML.Document): string = if isNull (box document) then nullArg "document" docx.WordProcessingML.Save document
- F# 5.0及以上版本可直接用
修改后重新编译DLL,C#项目引用后即可访问docx.Api模块及其中的公共成员。
问题2:当前做法的问题
你的猜测错误,DLL实际上已经包含了Api模块,但模块和其中的函数默认是internal可见性,C#项目作为外部程序集无法访问内部成员,因此看不到Api模块。
F#与C#的默认可见性规则有差异:C#中类默认是internal,但类型成员默认是private;而F#的模块、let绑定函数默认都是internal,仅type定义的类型默认是public。这就是你能看到WordProcessingML里的类型,但看不到Api模块的核心原因。
关于代码结构指南的时效性
你参考的这份代码结构指南核心思想依然有效,它强调的F#项目编译顺序、命名空间与模块的组织方式,至今仍是F#项目的最佳实践之一。仅在语法细节上有微小更新:F# 5.0之后支持了更简洁的public修饰符(之前只能用[<Public>]属性),但整体结构思路并未过时。
内容的提问来源于stack exchange,提问作者TimothyH

