本主题介绍适用于 F# Interactive (dotnet fsi) 指令的编译器指令,请参阅 使用 F# 的交互式编程。
编译器指令以 # 符号为前缀,并单独出现在行上。
下表列出了 F# 中可用的编译器指令。
| Directive | Description |
|---|---|
#if
if-expression |
支持条件编译。 在#if的计算结果为时,包含defined节中的内容。 |
#else |
支持条件编译。 如果上一个#if符号未计算出defined,则标记要包含的代码部分。 |
#endif |
支持条件编译。 标记条件代码段的末尾。 |
#[line] int、#[line] int字符串,#[line] int逐字字符串 |
指示原始源代码行和文件名以用于调试。 此功能是为生成 F# 源代码的工具而提供的。 |
#nowarn
warningcodes |
禁用由 警告码 指定的一个或多个编译器警告(请参阅下文)。 |
#warnon
warningcodes |
启用由 警告码 指定的一个或多个编译器警告(请参阅下文)。 |
条件编译指令
由这些指令之一停用的代码会在 Visual Studio Code 编辑器中显示为灰色。
以下代码演示了 #if、#else 和 #endif 指令的用法。 在此示例中,代码包含两个版本的 function1 定义。 当 VERSION1 使用 -define 编译器选项进行定义时,会激活 #if 指令与 #else 指令之间的代码。 否则,会激活 #else 与 #endif 之间的代码。
#if VERSION1
let function1 x y =
printfn "x: %d y: %d" x y
x + 2 * y
#else
let function1 x y =
printfn "x: %d y: %d" x y
x - 2*y
#endif
let result = function1 10 20
该 #if 指令还接受逻辑表达式:
#if SILVERLIGHT || COMPILED && (NETCOREFX || !DEBUG)
#endif
可以使用以下表达式。
| if-expr | 计算 |
|---|---|
if-expr1 \|\| if-expr2 |
defined 如果 if-expr1 或 if-expr2 为 defined。 |
if-expr1 && if-expr2 |
defined 如果 if-expr1 和 if-expr2 是 defined。 |
!if-expr1 |
如果defined不是if-expr1,则defined。 |
( if-expr1 ) |
如果 if-expr1 已定义,则这是已定义的。 |
symbol |
defined 如果它是被-define 编译器选项标记为已定义。 |
逻辑运算符具有通常的逻辑优先级。
F# 中没有 #define 编译器指令。 必须使用编译器选项或项目设置来定义 #if 指令使用的符号。
条件编译指令可以进行嵌套。 对于编译器指令,缩进并不重要。
预定义符号
F# 编译器和生成系统会自动定义多个可用于条件编译的符号。
生成配置符号
以下符号基于生成配置定义:
-
DEBUG:在调试模式下编译时定义。 在项目系统中,符号DEBUG会自动在调试配置中定义,但不在发布配置中定义。 此符号通常用于断言和诊断代码。 有关详细信息,请参阅 断言。 -
TRACE:为启用跟踪的生成定义。 同样DEBUG,此符号通常在调试配置中定义,但也可以在发布配置中启用。
可以使用编译器选项或项目设置替代这些值-define。
编译模式符号
以下符号区分不同的编译模式:
-
COMPILED:使用 F# 编译器编译代码时定义。 当需要代码在编译的程序集与 F# 交互式会话中表现不同时,此符号非常有用。 -
INTERACTIVE:在 F# Interactive (dotnet fsi)中编译或执行代码时定义,包括交互式会话和脚本执行。 这样,就可以编写在以交互方式运行时的工作方式不同的代码。
有关在脚本中使用这些符号的详细信息,请参阅 使用 F# 的交互式编程。
示例:
#if INTERACTIVE
// Code specific to F# Interactive
#r "nuget: Newtonsoft.Json"
#endif
#if COMPILED
// Code specific to compiled assemblies
open System.Configuration
#endif
目标框架符号
生成系统还为 SDK 样式项目中的不同目标框架定义预处理器符号。 创建面向多个 .NET 版本的库或应用程序时,这些符号非常有用。
| 目标框架 | 符号 | 其他符号 (在 .NET 5+ SDK 中可用) |
平台符号(仅 在指定特定于 OS 的 TFM 时可用) |
|---|---|---|---|
| .NET Framework |
NETFRAMEWORK、NET481、NET48、NET472、NET471、NET47、NET462、NET461、NET46、NET452、NET451、NET45、NET40、NET35、NET20 |
NET48_OR_GREATER、NET472_OR_GREATER、NET471_OR_GREATER、NET47_OR_GREATER、NET462_OR_GREATER、NET461_OR_GREATER、NET46_OR_GREATER、NET452_OR_GREATER、NET451_OR_GREATER、NET45_OR_GREATER、NET40_OR_GREATER、NET35_OR_GREATER、NET20_OR_GREATER |
|
| .NET Standard |
NETSTANDARD、NETSTANDARD2_1、NETSTANDARD2_0、NETSTANDARD1_6、NETSTANDARD1_5、NETSTANDARD1_4、NETSTANDARD1_3、NETSTANDARD1_2、NETSTANDARD1_1、NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER、NETSTANDARD2_0_OR_GREATER、NETSTANDARD1_6_OR_GREATER、NETSTANDARD1_5_OR_GREATER、NETSTANDARD1_4_OR_GREATER、NETSTANDARD1_3_OR_GREATER、NETSTANDARD1_2_OR_GREATER、NETSTANDARD1_1_OR_GREATER、NETSTANDARD1_0_OR_GREATER |
|
| .NET 5 及更高版本(和 .NET Core) |
NET、NET10_0、NET9_0、NET8_0、NET7_0、NET6_0、NET5_0、NETCOREAPP、NETCOREAPP3_1、NETCOREAPP3_0、NETCOREAPP2_2、NETCOREAPP2_1、NETCOREAPP2_0、NETCOREAPP1_1、NETCOREAPP1_0 |
NET10_0_OR_GREATER、NET9_0_OR_GREATER、NET8_0_OR_GREATER、NET7_0_OR_GREATER、NET6_0_OR_GREATER、NET5_0_OR_GREATER、NETCOREAPP3_1_OR_GREATER、NETCOREAPP3_0_OR_GREATER、NETCOREAPP2_2_OR_GREATER、NETCOREAPP2_1_OR_GREATER、NETCOREAPP2_0_OR_GREATER、NETCOREAPP1_1_OR_GREATER、NETCOREAPP1_0_OR_GREATER |
ANDROID、、BROWSERIOS、MACCATALYST、MACOS、TVOS、、 WINDOWS[OS][version](例如,IOS15_1),[OS][version]_OR_GREATER(例如,IOS15_1_OR_GREATER) |
注释
- 无论目标版本是什么,都将定义无版本符号。
- 仅针对目标版本定义特定于版本的符号。
- 为目标版本和所有早期版本定义
<framework>_OR_GREATER符号。 例如,如果针对 .NET Framework 2.0,则会定义以下符号:NET20、NET20_OR_GREATER、NET11_OR_GREATER和NET10_OR_GREATER。 -
NETSTANDARD<x>_<y>_OR_GREATER符号仅针对 .NET Standard 目标定义,而不适用于实现 .NET Standard 的目标,例如 .NET Core 和 .NET Framework。 - 它们与 MSBuild
TargetFramework属性和 NuGet 使用的目标框架名字对象 (TFM) 不同。
例如,可以使用这些符号根据目标框架有条件地编译代码:
#if NET6_0_OR_GREATER
// Use .NET 6+ specific APIs
#else
// Use alternative implementation for older frameworks
#endif
NULLABLE 指令
从 F# 9 开始,可以在项目中启用可为 null 的引用类型:
<Nullable>enable</Nullable>
这会自动将 NULLABLE 指令应用于构建。 在最初推出该功能时,通过 #if NULLABLE 哈希指令有条件地更改冲突代码非常有用:
#if NULLABLE
let length (arg: 'T when 'T: not null) =
Seq.length arg
#else
let length arg =
match arg with
| null -> -1
| s -> Seq.length s
#endif
行指令
在生成时,编译器会通过引用每个错误发生时所在的行号来报告 F# 代码中的错误。 这些行号从 1 开始(表示文件中的第一行)。 但是,如果要从另一种工具生成 F# 源代码,则生成的代码中的行号通常没什么意义,因为生成的 F# 代码中的错误很可能是由其他来源导致的。
#line 指令提供了一种方法,使生成 F# 源代码的工具的作者可以将有关原始行号和源文件的信息传递给生成的 F# 代码。
使用 #line 指令时,必须将文件名括在引号内。 除非原义标记 (@) 出现在字符串前面,否则必须使用两个反斜杠字符(而不是一个)对反斜杠字符进行转义,才能在路径中使用它们。 以下是有效的行标记。 在这些示例中,假设原始文件 Script1 会在通过某种工具运行时产生自动生成的 F# 代码文件,并且这些指令的位置处的代码是从文件 Script1 中第 25 行处的一些标记生成的。
# 25
#line 25
#line 25 "C:\\Projects\\MyProject\\MyProject\\Script1"
#line 25 @"C:\Projects\MyProject\MyProject\Script1"
# 25 @"C:\Projects\MyProject\MyProject\Script1"
这些标记指示在此位置生成的 F# 代码派生自位于 25 中第 Script1 行上或附近的一些构造。
请注意,#line指令不会影响#nowarn / #warnon . 这两个指令始终与正在编译的文件相关。
Warn 指令
警告指令对源文件的某些部分禁用或启用指定的编译器警告。
警告指令是一行源代码,其中包含
- 可选前导空格
- 字符串
#nowarn或#warnon - Whitespace
- 一个或多个 警告代码 (请参阅下文),用空格分隔
- 可选空格
- 可选行注释
警告码 是一系列数字(表示警告编号),可能前面有 FS,也可能用双引号括起来。
#nowarn 指令将禁用某个警告,直到发现相同警告号的 #warnon 指令,否则将一直禁用到文件结束。 同样,#nowarn 指令会禁用警告,直到找到针对相同警告编号的 #warnon 指令,或者直到文件结束。 编译默认值在此类对之前和之后适用,即
- 如果 --nowarn 编译器选项(或相应的 msbuild 属性)禁用,则不显示任何警告
- 除非启用了 --warnon 编译器选项(或相应的 msbuild 属性),否则不显示选择加入的警告。
下面是一个(已尝试的)示例。
module A
match None with None -> () // warning
let x =
#nowarn 25
match None with None -> 1 // no warning
#warnon FS25
match None with None -> () // warning
#nowarn "FS25" FS007 "42"
match None with None -> () // no warning