///
C# 源文件可以包含结构化注释,这些注释为这些文件中定义的类型生成 API 文档。C# 编译器生成一个 XML 文件,其中包含表示注释和 API 签名的结构化数据。例如,其他工具可以处理 XML 输出以网页或 PDF 文件的形式创建人工可读文档。
此过程为在代码中添加 API 文档提供了许多优势:
- C# 编译器将 C# 代码的结构与注释的文本合并到单个 XML 文档中。
- C# 编译器验证注释是否与相关标记的 API 签名匹配。
- 处理 XML 文档文件的工具可以定义特定于这些工具的 XML 元素和属性。
Visual Studio 等工具为文档注释中使用的许多常见 XML 元素提供 IntelliSense。
创建 XML 文档输出
通过编写三斜杠指示的特殊注释字段,为代码创建文档。注释字段包括描述注释后面的代码块的 XML 元素。 例如:
/// < summary >
/// 这个类执行一个重要的功能
/// < / summary >
public class Lei我的 { }
设置 GenerateDocumentationFile 或 DocumentFile 选项,编译器查找源代码中带有 XML 标记的所有注释字段,并从这些注释创建 XML 文档文件。启用此选项后,编译器将为项目中声明的任何公开可见成员生成 CS1591 警告,而无需 XML 文档注释。
XML 注释格式
XML 文档注释的使用需要分隔符来指示文档注释的开始和结束位置。您将以下分隔符用于 XML 文档标签:
- /// 单行分隔符:文档示例和 C# 项目模板使用此表单。如果空格位于分隔符后面,则 XML 输出中不包含该空格。
备注:在代码编辑器中键入 “ /// ” 分隔符后,Visual Studio 会自动插入 < summary > …… < /summary > 和标记并将光标置于这些标记中。可以在 “选项”对话框中打开或关闭此功能。 “/※※ ※/” 多行分隔符: “/※※ ※/” 分隔符具有以下格式规则:
- 在包含 “ /※※ ” 分隔符的行上,如果行的其余部分为空格,则不会为注释处理该行。如果分隔符后面的 “ /※※ ” 第一个字符为空格,则忽略该空格字符,并处理该行的其余部分。否则,分隔符后面的 “ /※※ ” 行的整个文本将作为注释的一部分进行处理。
- 在包含 “ ※/ ” 分隔符的行上,如果直到 “ ※/ ” 分隔符只有空格,则忽略该行。否则,将 “ ※/ ” 分隔符之前的行的文本作为注释的一部分进行处理。
- 对于以分隔符开头 “ /※※ ” 的行之后的行,编译器会在每行的开头查找一个通用模式。该模式可以包含可选的空格和 “ / ” 或星号 “ ※ ”,后跟更多可选空格。如果编译器在每行开头发现一个不以 “ /※※ ” 分隔符开头且不以 “ ※/ ” 分隔符结尾的通用模式,它会忽略该模式。
以下注释中将被处理的唯一部分是以 < summary > 开头的行。这三种标记格式生成相同的注释。
/** < summary > text < /summary > */ /** < summary > text < /summary > */ /** *< summary > text < /summary > */
编译器标识第二行和第三行开头的 “ * ” 的常见模式。该模式不包括在输出中。
/** * < summary > * text < /summary > */
编译器在以下注释中找不到常见模式,因为第三行的第二个字符不是星号。第二行和第三行中的所有文本都作为注释的一部分进行处理。
/** * < summary > text < /summary > */
编译器在以下注释中找不到模式,原因有两个。 首先,星号之前的空格数不一致。其次,第 5 行以制表符开头,这与空格不匹配。第二行至五行中的所有文本都作为批注的一部分进行处理。
/ ** * < summary > * text * text2 * < /summary > * /
若要引用 XML 元素(例如,函数处理要在 XML 文档注释中描述的特定 XML 元素),可以使用标准引用机制(< 以及 >)。若要在代码引用(cref)元素中引用泛型标识符,可以使用转义字符(例如)cref = "List<T>" 或大括号(cref = "List{T}")。作为特殊情况,编译器将大括号解析为尖括号,以便作者在引用泛型标识符时,文档注释显得不那么繁琐。
备注:上述正文中的 “※” 均为 “*”。
备注:如果使用单行 XML 注释分隔符编写注释,/// 但不包含任何标记,编译器会将这些注释的文本添加到 XML 输出文件。 但是,输出不包含 XML 元素,如 < summary >。使用 XML 注释(包括 Visual Studio IntelliSense)的大多数工具不会阅读这些注释。
接受 XML 文档输入的工具
以下工具从 XML 注释创建输出:
- DocFX:DocFX 是适用于 .NET 的 API 文档生成器,目前支持 C#、Visual Basic 和 F#。它还允许自定义生成的参考文档。DocFX 从源代码和 Markdown 文件生成静态 HTML 网站。此外,DocFX 还提供通过模板自定义网站的布局和样式的灵活性。还可以创建自定义模板。
- Sandcastle:Sandcastle 工具为包含概念页和 API 参考页的托管类库创建帮助文件。Sandcastle 工具基于命令行,没有 GUI 前端、项目管理功能或自动化生成过程。Sandcastle 帮助文件生成器提供独立的 GUI 和基于命令行的工具,以自动化方式生成帮助文件。Visual Studio 集成包也可用于它,以便可以从 Visual Studio 中完全创建和管理帮助项目。
- Doxygen:Doxygen 从一组记录的源文件生成联机文档浏览器(HTML)或脱机参考手册(在 LaTeX 中)。还支持在 RTF(MS Word)、PostScript、超链接 PDF、压缩 HTML、DocBook 和 Unix 手动页中生成输出。 可以将 Doxygen 配置为从未记录的源文件中提取代码结构。
备注:XML 文档注释不是元数据;它们不包括在编译的程序集中,因此无法通过反射访问它们。
ID 字符串
每个类型或成员都存储在输出 XML 文件中的元素中。其中每个元素都有一个唯一的 ID 字符串,用于标识类型或成员。ID 字符串必须考虑运算符、参数、返回值、泛型类型参数、refin 和 out 参数。若要对所有这些潜在元素进行编码,编译器遵循明确定义的规则来生成 ID 字符串。处理 XML 文件的程序使用 ID 字符串标识文档适用的相应 .NET 元数据或反射项。
编译器在生成 ID 字符串时会遵守以下规则:
- 字符串中没有空格。
字符串的第一部分使用单个字符后跟冒号标识成员的类型。使用以下成员类型:
字符 成员类型 注释 N namespace 不能向命名空间添加文档注释,但可以在支持的情况下创建对它们的 cref 引用。 T 类型 类型是类、接口、结构、枚举或委托。 F field P property 包括索引器或其他索引属性。 M 方法 包括特殊方法,如构造函数和运算符。 E event ! 错误字符串 其余字符串提供有关错误的信息。C# 编译器为无法解析的链接生成错误信息。 - 字符串的第二部分是项的完全限定名称,从命名空间的根目录开始。 项的名称、其封闭类型和命名空间通过句点进行分隔。如果项本身的名称有句点,则它们将被替换为哈希符号('#')。语法假定没有项直接在其名称中具有哈希符号。例如,String 构造函数的完全限定名称为 “ System . String . #ctor ”。
对于属性和方法,括在括号中的参数列表如下。如果没有参数,则不存在括号。参数用逗号分隔。每个参数的编码直接遵循在 .NET 签名中编码的方式:
- 基类型。 常规类型(ELEMENT_TYPE_CLASS 或 ELEMENT_TYPE_VALUETYPE)表示为类型的完全限定名称。
- 内部类型(例如 ELEMENT_TYPE_I4、ELEMENT_TYPE_OBJECTELEMENT_TYPE_STRING 和 ELEMENT_TYPE_TYPEDBYREFELEMENT_TYPE_VOID)表示为相应完整类型的完全限定名称。例如,System . Int32 或 System . TypedReference。
- ELEMENT_TYPE_PTR 的表示形式为:在修改类型之后添加 “ * ”。
- ELEMENT_TYPE_BYREF 的表示形式为:在修改类型之后添加 “ @ ”。
- ELEMENT_TYPE_CMOD_OPT 的表示形式为:在修改类型之后添加 “ ! ” 和修饰符类的完全限定名称。
- ELEMENT_TYPE_SZARRAY 在数组的元素类型之后表示为 “ [ ] ”。
- ELEMENT_TYPE_ARRAY 表示为 [ 下限:size , 下限:size ],其中逗号数为排名 -1,并且每个维度的下限和大小(如果已知)以十进制表示。如果未指定下限和大小,则会省略它们。如果省略特定维度的下限和大小,则也会省略 “ : ”。例如,一个下限为 1 且大小未指定的二维数组为 [ 1 : , 1 : ]。
- 仅对于转换运算符(op_Implicit 和 op_Explicit),方法的返回值将编码为 “ ~ ” 后跟返回类型。 例如,< member name = "M:System . Decimal . op_Explicit ( System . Decimal arg ) ~ System . Int32" > 是在 System . Decimal 类中声明的强制转换运算符 public static explicit operator int ( decimal value ); 的标记。
对于泛型类型,类型名称后跟反引号,然后是指示泛型类型参数数量的数字。例如:< member name = "T:SampleClass`2" > 是定义为 public class SampleClass < T , U > 的类型标记。对于采用泛型类型作为参数的方法,泛型类型参数被指定为以反杆开头的数字(例如 “0 ,” 1 )。每个数字表示类型泛型参数的从零开始的数组表示法。
- ELEMENT_TYPE_PINNED 的表示形式为:在修改类型之后添加 “ ^ ”。C# 编译器永远不会生成此编码。
- ELEMENT_TYPE_CMOD_REQ 被表示为 “ | ”,并在修改后的类型之后显示修饰符类的完全限定名称。C# 编译器永远不会生成此编码。
- ELEMENT_TYPE_GENERICARRAY 在数组的元素类型之后表示为 “ [ ? ] ”。C# 编译器永远不会生成此编码。
- ELEMENT_TYPE_FNPTR 表示为 “ = FUNC : type ( signature ) ”,其中 type 返回类型,签名是方法的参数。如果没有参数,则省略括号。C# 编译器永远不会生成此编码。
不表示下列签名组件,因为这些组件不用于区分重载方法:
- 调用约定
- 返回类型
- ELEMENT_TYPE_SENTINEL
以下示例演示如何生成类及其成员的 ID 字符串:
namespace 我的空间;
/// <summary>
/// 在这里输入类 我的类 的说明
/// ID 字符串生成:“T : 我的空间 . 我的类”
/// </summary>
public unsafe class 我的类
{
/// <summary>
/// 第一个构造器的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . #构造器”
/// </summary>
public 我的类 ( ) { }
/// <summary>
/// 第二个构造器的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . #构造器 ( System . Int32 )”
/// </summary>
/// < param name = "整数" > 整数参数的说明</param>
public 我的类 ( int i ) { }
/// <summary>
/// 字段 信息 的说明
/// ID 字符串生成:“F : 我的空间 . 我的类 . 信息”
/// </summary>
public string? 信息;
/// <summary>
/// PI 常数的说明
/// ID 字符串生成:“F : 我的空间 . 我的类 . PI”
/// </summary>
public const double PI = 3.14;
/// <summary>
/// 方法 Func 的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . Func”
/// </summary>
/// <returns>返回值的说明</returns>
public int Func ( ) => 1;
/// <summary>
/// 方法 SomeMethod 的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . SomeMethod ( System . String , System . Int32@ , System . Void* )”
/// </summary>
/// <param name="str">参数说明</param>
/// <param name="num">参数说明</param>
/// <param name="ptr">参数说明</param>
/// <returns>返回值说明</returns>
public int SomeMethod ( string str , ref int num , void* ptr ) { return 1; }
/// <summary>
/// 方法 AnotherMethod 的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . AnotherMethod ( System . Int16 [ ] , System . Int32 [ 0 : , 0 : ])”
/// </summary>
/// <param name="array1">参数说明</param>
/// <param name="array">参数说明</param>
/// <returns>返回值说明</returns>
public int AnotherMethod ( short [ ] array1 , int [ , ] array ) { return 0; }
/// <summary>
/// 操作符的说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . op_Addition ( 我的空间 . 我的类 , 我的空间 . 我的类 )”
/// </summary>
/// <param name="加数1">参数说明</param>
/// <param name="加数2">参数说明</param>
/// <returns>返回值说明</returns>
public static 我的类 operator + ( 我的类 加数1 , 我的类 加数2 ) { return 加数1; }
/// <summary>
/// 属性的说明
/// ID 字符串生成:“P : 我的空间 . 我的类 . 属性”
/// </summary>
public int 属性 { get { return 1; } set { } }
/// <summary>
/// 事件的说明
/// ID 字符串生成:“E : 我的空间 . 我的类 . OnHappened”
/// </summary>
public event Del? OnHappened;
/// <summary>
/// 索引器的说明
/// ID 字符串生成:“P : 我的空间 . 我的类 . Item ( System . String )”
/// </summary>
/// <param name="str">参数说明</param>
/// <returns></returns>
public int this [ string s ] => 1;
/// <summary>
/// 嵌套类 Nested 的说明
/// ID 字符串生成:“T : 我的空间 . 我的类 . Nested”
/// </summary>
public class Nested { }
/// <summary>
/// 委托的说明
/// ID 字符串生成:“T : 我的空间 . 我的类 . Del”
/// </summary>
/// <param name="i">参数说明</param>
public delegate void Del ( int i );
/// <summary>
/// 运算符说明
/// ID 字符串生成:“M : 我的空间 . 我的类 . op_Explicit ( 我的空间 . 我的类 )~System . Int32”
/// </summary>
/// <param name="我的参数">参数说明</param>
/// <returns>返回值说明</returns>
public static explicit operator int ( MyClass 我的参数 ) => 1;
}
// 和 /……/
C# 支持两种不同形式的注释。 单行注释以 // 开头,并在该代码行末尾结束。多行注释以 / 开头,以 / 结尾。下面的代码示例演示了每种注释:
// 这是单行注释
/* 这可能是这个类中所有代码的摘要。
您可以添加多个段落或链接到页面,比如https://learn.microsoft.com/dotnet/csharp。
你甚至可以加上表情符号。例如:🔥。
然后,当你完成的时候,关闭
* /
多行注释还可用于在代码行中插入文本。由于这些注释具有显式结束字符,因此可以在注释后面加上更多可执行代码:
public static double Chu ( double 被除数 , double 除数 )
{
return 被除数 /* 被除数 */ / 除数 /* 除数 */;
}
单行注释可以出现在同一行上的可执行代码之后。注释在文本行末尾结束:return Y++; // 将 Y 加一返回
弃用:_ 充当变量的占位符
字符 “ _ ” 用作 discard(弃用),它是未使用变量的占位符。
discard 令牌有两种用途:
声明一个未使用的变量。弃品无法读取或访问。
- 未使用的参数:var r = M ( out int , out var , out _ );
- 未使用的 lambda 表达式参数:Action < int > _ => WriteMessage ( );
- 未使用的解构参数:( int _ , var answer ) = M ( );
- 匹配丢弃模式中的任何表达式。你可以添加 “ _ ” 模式来满足穷尽性要求。
令牌在 C# 中是有效的标识符。只有在作用域中没有找到名为 “ ” 的有效标识符时,“ _ ” 令牌才会被解释为被丢弃。
弃集不能作为变量读取。如果你的代码读取了一个废弃的,编译器会报告一个错误。在某些安全的情况下,编译器可以避免为丢弃对象分配存储空间。
$ 字符串内插
$ 字符将字符串字面量标识为内插字符串。内插字符串是可能包含内插表达式的字符串文本。将内插字符串解析为结果字符串时,编译程序会将带有内插表达式的项替换为表达式结果的字符串表示形式。
字符串内插为格式化字符串提供了一种可读性和便捷性更高的方式。它比字符串复合格式设置更容易阅读。下面的示例使用了这两种功能生成同样的输出结果:
string XM = "赵老大";
DateTime RQ = DateTime . Now;
// 不使用内插
Console . WriteLine ( "你好,{0}!今天是 {1},现在是 {2:HH:mm}。" , XM , RQ . DayOfWeek , RQ );
// 使用内插
Console . WriteLine ( $"你好,{XM}!今天是 {RQ . DayOfWeek},现在是 {RQ:HH:mm}。" );
输出:
你好,赵老大!今天是 Thursday,现在是 12:43。
你好,赵老大!今天是 Thursday,现在是 12:43。
可以使用内插字符串初始化常量字符串。仅当内插字符串中的所有内插表达式也是常量字符串时,才可以执行此操作。
内插字符串的结构
若要将字符串标识为内插字符串,可在该字符串前面加上 $ 符号。字符串字面量开头的 $ 和 " 之间不能有任何空格。
具备内插表达式的项的结构如下所示:
{< 内插表达式 > [ , < 长度 >] [ : < 格式字符串 > ]}
括号中的元素是可选的。下表说明了每个元素:
元素 | 描述 |
---|---|
内插表达式 | 生成需要设置格式的结果的表达式。当表达式为 null 时,输出为空字符串(String . Empty ) |
width | 常数表达式,它的值定义表达式结果的字符串表示形式中的最小字符数。如果值为正,则字符串表示形式为右对齐;如果值为负,则为左对齐 |
格式字符串 | 受表达式结果类型支持的格式字符串 |
以下示例使用上表中所描述的可选格式设置组件:
Console . WriteLine ( $"| {"左" , -17} | {"右" , -30}|" );
const int Z列宽 = 20;
Console . WriteLine ( $"{Math . PI , Z列宽} - Math . PI 的默认的格式" );
Console . WriteLine ( $"{Math . PI , Z列宽:F3} - 只显示 Math . PI 的三位小数" );
从 C# 11 开始,可以在内插表达式中使用换行符,以使表达式的代码更具可读性。下面的示例展示了换行符如何提高涉及模式匹配的表达式的可读性:
int 安全分数 = 60;
string message = $"{安全分数} 的使用策略:{
安全分数 switch
{
> 90 => "无限制使用",
> 80 => "一般使用,每日安全检查",
> 70 => "问题必须在一周内解决",
> 50 => "问题必须在一天内解决",
_ => "在使用前必须解决问题",
}
}";
Console . WriteLine ( message );
内插原始字符串字面量
从 C# 11 开始,可以使用内插原始字符串字面量,如以下示例所示:
int X = 2;
int Y = 3;
var ZD原距 = $"""坐标为 " {X},{Y} " 与原点的距离为:{Math . Sqrt ( X * X + Y * Y ):F3}。""";
Console . WriteLine ( ZD原距 );
要在结果字符串中嵌入 “ { ” 和 “ } ” 字符,可以用多个 $ 字符开始插入原始字符串。这样做时,任何短于 $ 字符数量的 “ { ” 或 “ } ” 字符序列都会嵌入到结果字符串中。要在字符串中包含任何插值表达式,需要使用与 $ 字符数量相同的花括号,如下面的例子所示:
<http://>
int X = 2;
int Y = 3;
var 原距 = $$"""{坐标为 {{{X}}, {{Y}}} 与原点的距离为:{{Math . Sqrt ( X * X + Y * Y ):F3}}。}""";
Console . WriteLine ( 原距 );
在前面的示例中,内插原始字符串字面量以两个 $ 字符开头。你需要将每个内插表达式放在双大括号({{ 和 }})之间。单个大括号嵌入到结果字符串中。如果需要将重复的 “ { ” 或 “ } ” 字符嵌入结果字符串中,请使用相应增加的 $ 字符数来指定内插原始字符串字面量。如果字符串文本的重复大括号数多于 $ 字符数,则 “ { ” 和 “ } ” 字符将从内到外分组。在前面的示例中,“ 文本坐标为 {{{X}}, {{Y}}} ” 将 “ {{X}} ” 和 “ {{Y}} ” 解释为内插表达式。输出字符串中完整地包含外部 “ { ” 和 “ } ”。
特殊字符
要在内插字符串生成的文本中包含大括号 “ { ” 或 “ } ”,请使用两个大括号,即 “ {{ ” 或 “ }} ”。
因为冒号(“ : ”)在内插表达式项中具有特殊含义,为了在内插表达式中使用条件运算符,请将该表达式放在括号内。
以下示例演示了如何在结果字符串中包括大括号。它还演示了如何使用条件运算符:
string XM = "玛丽安娜";
int NL = 19;
Console . WriteLine ( $"他问,\"你的名字“ {XM}?”\"-{{" );
Console . WriteLine ( $"{XM}的年龄 {NL}{(NL == 1 ? "" : "s")} 岁 。" );
内插逐字字符串以 $ 和 @ 字符开头。可以按任意顺序使用 $ 和 @:$@"……" 和 @$"……" 均为有效的内插逐字字符串。
特定于区域性的格式设置
默认情况下,内插字符串将 CultureInfo . CurrentCulture 属性定义的当前区域性用于所有格式设置操作。
要将插值字符串解析为特定文化的结果字符串,请使用 String . Create ( IFormatProvider , DefaultInterpolatedStringHandler ) 方法,该方法从 .NET 6 开始提供。下面的示例演示如何执行此操作:
double SJD光速 = 299792.458;
CultureInfo . CurrentCulture = CultureInfo . GetCultureInfo ( "nl-NL" );
string zfc荷兰 = $"光速({CultureInfo . CurrentCulture . NativeName})= {SJD光速:N3}";
Console . WriteLine ( zfc荷兰 );
CultureInfo QY = CultureInfo . GetCultureInfo ( "en-IN" );
string zfc印度 = string . Create ( QY , $"光速({QY . NativeName})= {SJD光速:N3}" );
Console . WriteLine ( zfc印度 );
string zfc不变 = string . Create ( CultureInfo . InvariantCulture , $"光速({CultureInfo . InvariantCulture . NativeName})= {SJD光速:N3}" );
Console . WriteLine ( zfc不变 );
在 .NET 5 及更早的 .NET 版本中,使用内插字符串隐式转换为 FormattableString 实例。然后,可以使用实例 FormattableString . ToString ( IFormatProvider ) 方法或静态 FormattableString . Invariant 方法来生成特定于区域性的结果字符串。
内插字符串编译
编译器检查内插字符串是否分配给满足内插字符串处理程序模式的类型。内插字符串处理程序是一种将内插字符串转换为结果字符串的类型。当内插字符串的类型为 string 时,它由 System . Runtime . CompilerServices . DefaultInterpolatedStringHandler 处理。使用内插字符串处理程序是一种高级方案,通常出于性能原因而需要使用。
备注:内插字符串处理程序的一个副作用是,自定义处理程序(包括 System . Runtime . CompilerServices . DefaultInterpolatedStringHandler)可能不会在所有条件下计算内插字符串中的所有内插表达式。这意味着这些表达式的副作用可能不会发生。
如果内插字符串具有 string 类型,它通常转换为 String . Format 方法调用。如果分析的行为等同于串联,则编译器可将 String . Format 替换为 String . Concat。
如果内插字符串类型为 IFormattable 或 FormattableString,则编译器会生成对 FormattableStringFactory . Create 方法的调用。
逐字文本 - @ 在变量、属性和字符串文本中
@ 特殊字符用作原义标识符。通过以下方式使用它:
指示将原义解释字符串。@ 字符在此实例中定义原义标识符。简单转义序列(如代表反斜杠的 "\")、十六进制转义序列(如代表大写字母 A 的 "\x0041")和 Unicode 转义序列(如代表大写字母 A 的 "\u0041")都将按字面解释。只有引号转义序列("")不会按字面解释;因为它生成一个双引号。此外,如果是逐字内插字符串,大括号转义序列({{ 和 }})不按字面解释;它们会生成单个大括号字符。下面的示例分别使用常规字符串和原义字符串定义两个相同的文件路径。这是原义字符串的较常见用法之一。
string zfcLJ1 = @"C:\Docs\Text\u0066.txt"; string zfcLJ2 = "C:\\Docs\\Text\\u0066.txt";
两者都是:C:\Docs\Text\u0066.txt
下面的示例演示定义包含相同字符序列的常规字符串和原义字符串的效果。string s1 = "He said, \"This is the last \u0063hance\x0021\""; string s2 = @"He said, ""This is the last \u0063hance\x0021"""; Console . WriteLine ( s1 ); Console . WriteLine ( s2 );
上例输出:
He said, "This is the last chance!"
He said, "This is the last \u0063hance\x0021"使用 C# 关键字作为标识符。@ 字符可作为代码元素的前缀,编译器将把此代码元素解释为标识符而非 C# 关键字。下面的示例使用 @ 字符定义其在 for 循环中使用的名为 for 的标识符。
string [ ] @for = { "小红" , "小苏" , "小黄" , "小黑" }; for ( int Z = 0 ; Z < @for . Length ; Z++ ) { Console . WriteLine ( $"这是你的礼物:{@for [ Z ]}" ); }
上例输出:
这是你的礼物:小红
这是你的礼物:小苏
这是你的礼物:小黄
这是你的礼物:小黑- 使编译器在命名冲突的情况下区分两种属性。属性是派生自 Attribute 的类。其类型名称通常包含后缀 Attribute,但编译器不会强制进行此转换。随后可在代码中按其完整类型名称(例如 [ InfoAttribute ])或短名称(例如 [ Info ])引用此属性。但是,如果两个短名称相同,并且一个类型名称包含 Attribute 后缀而另一类型名称不包含,则会出现命名冲突。例如,由于编译器无法确定将 Info 还是 InfoAttribute 属性应用于 Example 类,因此下面的代码无法编译。
[AttributeUsage ( AttributeTargets . Class )]
public class LeiInfo : Attribute
{
private string zfc信息;
public LeiInfo ( string 信息 )
{
zfc信息 = 信息;
}
}
[AttributeUsage ( AttributeTargets . Method )]
public class LeiInfoAttribute : Attribute
{
private string zfc信息;
public LeiInfoAttribute ( string 信息 )
{
zfc信息 = 信息;
}
}
[LeiInfo ( "例子:" )] // 警告 CS1614:“LeiInfo”与“LeiInfoAttribute”之间的“LeiInfo”不明确。请使用“@LeiInfo”或明确包含“属性”后缀
internal class Program
{
[LeiInfoAttribute ( "入口点……" )]
public static void Main ( string [ ] args )
{
Console . WriteLine ( );
}
}
原始字符串字面量 - 字符串字面量中的 """(至少三个引号)
原始字符串字面量以至少三个双引号(")字符开始和结束:
string zfc单行 = """这是一个 "原始字符串字面量"。它可以包含 “\”、“'” 和 “"” 这样的字符。""";
原始字符串字面量可以包含多行:
var xml = """
<element attr="content">
<body>
</body>
</element>
""";
下面的规则支配着对多行原始字符串字面量的解释:
- 起始引号必须是对应行的最后一个非注释标记,结束引号必须是对应行的第一个非注释标记。
- 从原始字符串字面量的所有行中删除结束引号左侧的任何空白。
- 同一行中起始引号后的空白将被忽略。
- 只有左引号之后的行包含在字符串字面量中。
- 如果同一行的结束分隔符之前有空格(WhiteSpace)或水平制表符(Tab),则在每一行的开头必须存在空格的确切数量和类型(包括空格和水平制表符)。具体来说,空格不能匹配水平制表符,反之亦然。
- 结束引号之前的换行符没有包含在字符串字面量中。
你可能需要创建一个包含三个或更多连续双引号字符的原始字符串字面量。原始字符串字面量可以以至少三个双引号字符的序列开始和结束。如果字符串字面量包含三个连续的双引号,则原始字符串字面量的开始和结束都是四个双引号:
string zfc许多引号 = """" 如你所见,"""原始字符串字面量""" 在需要时可以以三个以上的双引号开始和结束。"""";
如果需要以引号字符开始或结束原始字符串字面量,请使用多行格式:
string zfc多行许多引号 = """"
"""原始字符串字面量""" 在需要时可以以三个以上的双引号开始和结束。
"""";
原始字符串字面量还可以与内插字符串结合使用,以在输出字符串中嵌入 “{” 和 “}” 字符。 在内插的原始字符串字面量中使用多个 “$” 字符,以在输出字符串中嵌入 “{” 和 “}” 字符,而无需对这些字符进行转义。
原始字符串字面量的内容不得包含一组长度等于或大于原始字符串字面量分隔符长度的连续 “"” 字符。 例如,字符串 “"""" """ """"” 和 “""""""" """""" """"" """" """ """""""” 的格式就很标准。 但是,字符串 “""" """ """” 和 “""" """" """” 的格式就不正确。
原始字符串字面量是在 C# 11 中引入的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。