随着.NET Core 3.0 Preview 7的发布,C#8.0已被认为是“功能完整”的。这意味着它们的最大亮点Nullable Reference Types,在行为方面也被锁定在.NET Core版本中。它将在C#8.0之后继续改进,但现在可以认为它与C#8.0的其余部分一样是稳定的。

目前,我们的目标是尽可能多地收集关于可空性使用过程中的反馈以发现问题,同时收集有关在.NET Core 3.0之后我们可以做的功能的进一步改进的反馈。这是有史以来为C#构建的最大功能之一,尽管我们已尽力做好它,但我们仍然需要您的帮助!

正是基于这样的交叉点,我们特别呼吁.NET库作者们尝试使用该功能并开始注解您的库。我们很乐意听取您的反馈并帮助解决您所遇到的任何问题。

熟悉该功能

我们建议您在使用该功能之前,先阅读一下Nullable Reference Types文档,它包含以下功能点:

  • 概念性概述
  • 如何指定可为空的引用类型
  • 如何控制编译器分析或覆盖编译器分析

如果您还不熟悉这些概念,请在继续操作之前快速阅读文档。

为您的库采用可空性的第一步是放开Nullable约束。具体步骤:

确保您使用的是C#8.0

如果您的库是基于netcoreapp3.0的,默认情况下将使用C#8.0。当我们发布预览8时,如果你是基于netstandard2.1构建,那么默认情况也将使用C#8.0 。

.NET Standard本身还没有任何可空的注解。如果您的目标是.NET Standard,即使您不需要.NET Core特定的API,您仍然可以使用.NET标准和NetCoreApp3.0的多目标。好处是编译器将使用CoreFX中的可空注解来帮助您(在.NET Standard项目中)正确的获取自己的注解。

如果由于某种原因无法更新TFM,可以LangVersion明确设置:

   1:  <PropertyGroup>
   2:   
   3:  <LangVersion>8.0</LangVersion>
   4:   
   5:  </PropertyGroup>

请注意,C#8.0不适用于较旧的Framework Target,例如.NET Core 2.x或.NET Framework 4.x. 因此,除非您的目标是.NET Core 3.0或.NET Standard 2.1,否则其他语言(版本)功能可能无法使用。

建议采用两种通用方法来采用可空性

选择项目,选择退出文件

此方法最适用于新文件频繁添加的项目,过程很简单:

1、以下属性应用于项目文件:

   1:  <PropertyGroup>
   2:      <Nullable>enable</Nullable>
   3:  </PropertyGroup>

2、通过将此项添加到项目中每个现有文件的顶部,可以(选择性)用该项目的每个文件中的可空性:

   1:  #nullable disable

3、择一个文件,删除该#nullable disable指令,然后修复警告。重复操作直到所有#nullable disable指令都被删除。

这种方法需要更多的前期工作,但这意味着您可以在移植时继续在库中工作,并确保任何新文件自动选择为可空性。这是我们通常建议的方法,我们目前在一些自己的代码库中使用它。

一次选择一个文件

这种方法与前一种方法相反。

1、通过将此项添加到文件顶部,为项目的文件启用可空性:

   1:  #nullable disable

2、继续将其添加到其他文件中,直到所有文件都被注释并且所有可空性警告都得到解决。

3、将以下属性应用于项目文件:

   1:  <PropertyGroup>
   2:      <Nullable>enable</Nullable>
   3:  </PropertyGroup>

4、删除#nullable enable源中的所有指令。

这种方法最终需要更多工作,但它允许您立即开始修复可空性警告。

请注意,如果更适合您的工作流程,您还可以将该Nullable属性应用于Directory.build.props文件。

Preview7的Nullable引用类型有哪些新功能

该功能最重要的就是补充了用于处理泛型和更高级的API使用场景的工具。这些源于我们注解.NET Core的经验。

notnull泛型约束

通常情况下,泛型是不允许为空的,如以下跟定接口:

   1:  interface IDoStuff<TIn, TOut>
   2:  {
   3:      TOut DoStuff(TIn input);
   4:  }

您可能希望仅支持不可为空的引用类型和值类型。所以代替string和int会好一点,但是如果使用了string?和int?就不应被代替了:

可以使用notnull约束来实现:

   1:  #nullable enable
   2:   
   3:  interface IDoStuff<TIn, TOut>
   4:      where TIn : notnull
   5:      where TOut : notnull
   6:  {
   7:      TOut DoStuff(TIn input);
   8:  }

如果实现类没有同样应用notnull约束,就会报出以下警告:

   1:  // Warning: CS8714 - Nullability of type argument 'TIn' doesn't match 'notnull' constraint.
   2:  // Warning: CS8714 - Nullability of type argument 'TOut' doesn't match 'notnull' constraint.
   3:  public class DoStuffer<TIn, TOut> : IDoStuff<TIn, TOut>
   4:  {
   5:      public TOut DoStuff(TIn input)
   6:      {
   7:          ...
   8:      }
   9:  }

为了修复这些警告,需要应用同样的约束:

   1:  // No warnings!
   2:  public class DoStuffer<TIn, TOut> : IDoStuff<TIn, TOut>
   3:      where TIn : notnull
   4:      where TOut : notnull
   5:  {
   6:      TOut DoStuff(TIn input)
   7:      {
   8:          ...
   9:      }
  10:  }

当我们为那个类创建实例的时候,如果你使用了nullable引用类型,也会发生警告:

   1:  // Warning: CS8714 - Nullability of type argument 'string?' doesn't match 'notnull' constraint
   2:  var doStuffer = new DoStuff<string?, string?>();
   3:   
   4:  // No warnings!
   5:  var doStufferRight = new DoStuff<string, string>();

(上述警告)也适用于值类型:

   1:  // Warning: CS8714 - Nullability of type argument 'int?' doesn't match 'notnull' constraint
   2:  var doStuffer = new DoStuff<int?, int?>();
   3:   
   4:  // No warnings!
   5:  var doStufferRight = new DoStuff<int, int>();

对于那些您只想使用非空引用类型的泛型来说,这些约束是非常有用的。一个突出例子就是Dictionary<TKey, TValue>,TKey是空约束,TValue是非空约束

   1:  // Warning: CS8714 - Nullability of type argument 'string?' doesn't match 'notnull' constraint
   2:  var d1 = new Dictionary<string?, string>(10);
   3:   
   4:  // And as expected, using 'null' as a key for a non-nullable key type is a warning...
   5:  var d2 = new Dictionary<string, string>(10);
   6:   
   7:  // Warning: CS8625 - Cannot convert to non-nullable reference type.
   8:  var nothing = d2[null];

然而,并非所有泛型的可空性问题都可以通过这种方式解决。这是我们添加一些新属性以允许您在编译器中进行可空分析影响的地方。

T?的问题

你想知道:为什么在指定可以用可空引用或值类型替换的泛型类型时“只”允许T?。不幸的是,答案很复杂。

通常T?意味着“任何可以为空的类型”。同时这意味着这T将意味着“任何非可空类型”,这不是真的!今天可以用可空值类型替换T (例如bool?)。这是因为T已经是一个不受约束的泛型类型。语义的这种变化可能是意料之外的,并且对于T用作无约束泛型类型的大量现有代码而言会引起一些悲痛。

其次,有一点非常重要就是,要注意可空引用类型和可空值类型是不一样的。可以为Null的值类型映射到.NET中的具体类类型。所以int?实际上是Nullable<int>。但是string?,它实际上是相同的,string但有一个编译器生成的属性来注解它。这样做是为了向后兼容。换句话说,string?是一种假象,而int?不是。

可空值类型和可空引用类型之间的区别出现在以下模式中:

   1:  void M<T>(T? t) where T: notnull

这意味着该参数是可以为空的,并且T被约束为notnull。如果Tstring,则实际签名M将是M<string>([NullableAttribute] T t),但如果T是a int,那么M将是M<int>(Nullable<int> t)。这两个签名根本不同,而且这种差异是不可调和的。

由于可空引用类型和可空值类型的具体表示之间存在此问题,因此任何使用都T?必须要求您将其约束Tclass或者struct

您可能希望在一个方向上允许可以为空的类型(例如,仅作为输入或输出),并且不可以用notnull或t和t?表达。除非人为地为输入和输出添加单独的泛型类型,否则就需要拆分。

Nullable的先决条件:AllowNull and DisallowNull

考虑如下代码:

   1:  public class MyClass
   2:  {
   3:      public string MyValue { get; set; }
   4:  }

这可能是我们在C#8.0之前支持的API。但是,string的含义现在意味着不可空string!我们可能希望实际上仍然允许null值,但总是会采用get返回string值。在这里使用AllowNull可能会让你感到有点迷惑:

   1:  public class MyClass
   2:  {
   3:      private string _innerValue = string.Empty;
   4:   
   5:      [AllowNull]
   6:      public string MyValue
   7:      {
   8:          get
   9:          {
  10:              return _innerValue;
  11:          }
  12:          set
  13:          {
  14:              _innerValue = value ?? string.Empty;
  15:          }
  16:      }
  17:  }

因为我们总是确保getter没有空值,所以我希望保留类型string。但为了向后兼容,我们仍然要接受空值。allownull属性允许您指定setter接受空值。然后,调用方会像您预期的那样受到影响:

   1:  void M1(MyClass mc)
   2:  {
   3:      mc.MyValue = null; // Allowed because of AllowNull
   4:  }
   5:   
   6:  void M2(MyClass mc)
   7:  {
   8:      Console.WriteLine(mc.MyValue.Length); // Also allowed, note there is no warning
   9:  }

注意:当前有一个bug,其中空值的赋值与可空分析存在冲突。这将在将来的编译器更新中解决。

考虑另一个API:

   1:   
   2:  public static HandleMethods
   3:  {
   4:      public static void DisposeAndClear(ref MyHandle handle)
   5:      {
   6:          ...
   7:      }
   8:  }

在这种情况下,MyHandle指向的是资源句柄。这个API的典型用途是我们有一个非null实例,通过引用传递,但是当它被清除时,引用是null。这会幻读并用以下方式表示DisallowNull

   1:  public static HandleMethods
   2:  {
   3:      public static void DisposeAndClear([DisallowNull] ref MyHandle? handle)
   4:      {
   5:          ...
   6:      }
   7:  }

如果调用方传递空值,会发出警告来告诉调用方,但如果在调用方法后尝试“点”到句柄中,则会发出警告:

   1:  void M(MyHandle handle)
   2:  {
   3:      MyHandle? local = null; // Create a null value here
   4:      HandleMethods.DisposeAndClear(ref local); // Warning: CS8601 - Possible null reference assignment
   5:      
   6:      // Now pass the non-null handle
   7:      HandleMethods.DisposeAndClear(ref handle); // No warning! ... But the value could be null now
   8:      
   9:      Console.WriteLine(handle.SomeProperty); // Warning: CS8602 - Dereference of a possibly null reference
  10:  }

这两个属性允许我们在需要它们的情况下使用单向可空性或不可空性。

更正式的:

AllowNull属性允许调用方传递空值,即使该类型不允许这样做。DisAllowNull属性不允许调用方传递null,即使该类型允许。它们可以在接受输入的任何内容上指定:

  • 值参数
  • in 标记的参数
  • ref 标记的参数
  • 字段
  • 属性
  • 索引

要点:这些属性仅影响使用它们注解的调用者的方法的可空分析。注解的方法主体和接口实现类这些并不支持这些属性。我们将来可能会对此提供支持。

可空的后置条件:MaybeNullNotNull

考虑一下范例API:

   1:  public class MyArray
   2:  {
   3:      // Result is the default of T if no match is found
   4:      public static T Find<T>(T[] array, Func<T, bool> match)
   5:      {
   6:          ...
   7:      }
   8:   
   9:      // Never gives back a null when called
  10:      public static void Resize<T>(ref T[] array, int newSize)
  11:      {
  12:          ...
  13:      }
  14:  }

这里还有一个问题。对于引用类型为空的情况,如果Find()方法返回不出来内容,我们希望返回默认值。我们希望Resize以接受可能为空的输入,但我们希望确保Resize调用的时候,引用传递的数组值始终为非空。又一次,应用NotNull约束并不能解决这个问题。哎!!

现在我们可以想象一下输出的可空性!可以这样修改示例:

   1:  public class MyArray
   2:  {
   3:      // Result is the default of T if no match is found
   4:      [return: MaybeNull]
   5:      public static T Find<T>(T[] array, Func<T, bool> match)
   6:      {
   7:          ...
   8:      }
   9:   
  10:      // Never gives back a null when called
  11:      public static void Resize<T>([NotNull] ref T[]? array, int newSize)
  12:      {
  13:          ...
  14:      }
  15:  }

现在这些可以影响调用方:

   1:  void M(string[] testArray)
   2:  {
   3:      var value = MyArray.Find<string>(testArray, s => s == "Hello!");
   4:      Console.WriteLine(value.Length); // Warning: Dereference of a possibly null reference.
   5:   
   6:      MyArray.Resize<string>(ref testArray, 200);
   7:      Console.WriteLine(testArray.Length); // Safe!
   8:  }

第一个方法指定返回的T可以是空值。这意味着此方法的调用方在使用其结果时必须检查是否为空。

第二个方法有一个更复杂的签名: [NotNull] ref T[]? 数组。这意味着作为输入的数组可以为空,但当调用Resize时,数组不可以为空。这意味着,如果您在调用Resize后“点”到数组中,将不会收到警告。但调用Resize后,数组将不再为空。

后置条件:MaybeNullWhen(bool)NotNullWhen(bool)

该类除了实现Load方法外,还会根据ReloadOnChange属性,在构造函数中注册OnChange事件,用于重新加载配置信息,源码如下:

请考虑如下示例:

   1:  public class MyString
   2:  {
   3:      // True when 'value' is null
   4:      public static bool IsNullOrEmpty(string? value)
   5:      {
   6:          ...
   7:      }
   8:  }
   9:   
  10:  public class MyVersion
  11:  {
  12:      // If it parses successfully, the Version will not be null.
  13:      public static bool TryParse(string? input, out Version? version)
  14:      {
  15:          ...
  16:      }
  17:  }
  18:   
  19:  public class MyQueue<T>
  20:  {
  21:      // 'result' could be null if we couldn't Dequeue it.
  22:      public bool TryDequeue(out T result)
  23:      {
  24:          ...
  25:      }
  26:  }

以上方法在.NET中随处可见,其中true或false的返回值对应于参数的可空性(或可能的可空性)。MyQueue案例也有点特殊,因为它是通用的。如果结果为false,则TrydeQueue应为result提供空值,但仅当T是引用类型时才提供空值。如果T是一个结构体,则它不会为空。

所以,我想做以下三件事情:

  1. 如果IsNullOrEmpty返回false, 那么值为非空
  2. 如果TryParse返回true, 那么version为非空
  3. 如果TryDequeue返回false, 那么result可以是null, 前提是它是引用类型

不幸的是,C编译器不会将方法的返回值与其某个参数的可空性相关联!

输入NotNullWhen(bool)和MaybeNullWhen(bool). 现在,我们可以用以下参数更进一步:

   1:  public class MyString
   2:  {
   3:      // True when 'value' is null
   4:      public static bool IsNullOrEmpty([NotNullWhen(false)] string? value)
   5:      {
   6:          ...
   7:      }
   8:  }
   9:   
  10:  public class MyVersion
  11:  {
  12:      // If it parses successfully, the Version will not be null.
  13:      public static bool TryParse(string? input, [NotNullWhen(true)] out Version? version)
  14:      {
  15:          ...
  16:      }
  17:  }
  18:   
  19:  public class MyQueue<T>
  20:  {
  21:      // 'result' could be null if we couldn't Dequeue it.
  22:      public bool TryDequeue([MaybeNullWhen(false)] out T result)
  23:      {
  24:          ...
  25:      }
  26:  }

可以影响到调用方:

   1:  void StringTest(string? s)
   2:  {
   3:      if (MyString.IsNullOrEmpty(s))
   4:      {
   5:          // This would generate a warning:
   6:          // Console.WriteLine(s.Length);
   7:          return;
   8:      }
   9:   
  10:      Console.WriteLine(s.Length); // Safe!
  11:  }
  12:   
  13:  void VersionTest(string? s)
  14:  {
  15:      if (!MyVersion.TryParse(s, out var version))
  16:      {
  17:          // This would generate a warning:
  18:          // Console.WriteLine(version.Major);
  19:          return;
  20:      }
  21:   
  22:      Console.WriteLine(version.Major); // Safe!
  23:  }
  24:   
  25:  void QueueTest(MyQueue<string> q)
  26:  {
  27:      if (!q.TryDequeue(out var s))
  28:      {
  29:          // This would generate a warning:
  30:          // Console.WriteLine(s.Length);
  31:          return;
  32:      }
  33:   
  34:      Console.WriteLine(s.Length); // Safe!
  35:  }

这使得调用者可以使用与以前相同的模式来处理API,而不需要编译器发出任何假的警告:

  • 如果IsNullOrEmpty是true, “点”进去就是安全的
  • 如果TryParse是true, version会被解析并被安全“点”进去
  • 如果TryDequeue是false, 则结果可能为空,需要进行检查(例如:当类型为结构体时返回false为非空,而对于引用类型为false则意味着它可能为空)

NotNullWhen(bool)表示即使类型允许,参数也不能为空,条件是该方法的bool返回值。MaybeNullWhen(bool)表示即使类型不允许参数为空,参数也可以为空,条件也是该方法的bool返回值。它们可以在任何参数类型上指定。

输入和输出之间的空相关性

NotNullIfNotNull(string)

如下范例

   1:  class MyPath
   2:  {
   3:      public static string? GetFileName(string? path)
   4:      {
   5:          ...
   6:      }
   7:  }

在这种情况下,我们希望返回一个可能为空的字符串,并且我们还应该能够接受一个空值作为输入。所以这个方法签名完成了我想要表达的。

但是,如果路径不为空,我们希望确保始终返回一个字符串。也就是说,我们希望getFileName的返回值不为空,以路径为空为条件。这是无法表达的。

输入NotNullIfNotNull(字符串)。这个属性可以使您的代码异常复杂,所以小心使用它!以下是在我的API中使用它的方法:

   1:  class MyPath
   2:  {
   3:      [return: NotNullIfNotNull("path")]
   4:      public static string? GetFileName(string? path)
   5:      {
   6:          ...
   7:      }
   8:  }

对调用方的影响

   1:  void PathTest(string? path)
   2:  {
   3:      var possiblyNullPath = MyPath.GetFileName(path);
   4:      Console.WriteLine(possiblyNullPath.Length); // Warning: Dereference of a possibly null reference
   5:      
   6:      if (!string.IsNullOrEmpty(path))
   7:      {
   8:          var goodPath = MyPath.GetFileName(path);
   9:          Console.WriteLine(goodPath.Length); // Safe!
  10:      }
  11:  }

NotNullIfNotNull(string)属性表示任何输出值都是非空的,条件是指定名称的给定参数可以为空。可以参考如下指定:

  • 方法返回值
  • ref标记的参数

流特性:DoesNotReturnDoesNotReturnIf(bool)

您可以您的程序中使用影响控制流的多种方法。例如,一个异常帮助器方法,如果调用,它将引发异常;或者一个断言方法,如果输入为真或假,它将引发异常。

您可能希望做一些类似断言一个值是非空的事情,我们认为如果编译器能够理解的话,您也会喜欢它。

输入DoesNotReturn 和DoesNotReturnIf(bool)。下面是一个示例,可以选用以下两种方法之一:

   1:  internal static class ThrowHelper
   2:  {
   3:      [DoesNotReturn]
   4:      public static void ThrowArgumentNullException(ExceptionArgument arg)
   5:      {
   6:          ...
   7:      }
   8:  }
   9:   
  10:  public static class MyAssertionLibrary
  11:  {
  12:      public static void MyAssert([DoesNotReturnIf(false)] bool condition)
  13:      {
  14:          ...
  15:      }
  16:  }

当在方法中调用ThrowArgumentNullException时,它将引发异常。DoesNotReturn向编译器发出一个信号,说明在该点之后不需要进行可以为空的分析,因为代码是不可访问的。

当调用MyAssert并且传递给它的条件为false时,它将引发异常。条件参数使用了DoesNotReturnIf(false)注解以使编译器知道,如果条件为false,程序流将不会继续。如果要断言值的可空性,这将很有用。在MyAssert后面的代码路径中(值!=null);编译器可以假定值不是null。

不能在方法上使用DoesNotReturn。 DoesNotReturnIf(bool)可用于输入参数。

注解的演进

一旦注解了公共API,您将需要考虑更新API可能会产生下游影响的情况:

  • 在没有任何注解的地方添加可为空的注释可能会给用户代码带来警告。
  • 删除可为空的注释也会引入警告(例如,接口实现)

可以为空的注解是公共API不可分割的一部分。添加或删除注解会引入新的警告。我们建议从预览版开始,在预览版中征求反馈意见,目的是在完整发布后不更改任何注解。虽然通常情况下不太可能,但我们还是建议这样做。

Microsoft框架和库的当前状态

因为可以为空的引用类型是新的,所以大多数微软编写的C#框架和库还没有被适当的注解。

也就是说,.NET Core的“Core Lib”部分(约占.NET核心共享框架的20%)已经完全更新。它包括诸如System、System.IO和System.Collections.Generic这样的名称空间。我们正在寻找对我们这些决策的反馈,以便我们能够在它们的广泛之前尽快做出适当的调整。

尽管仍有约80%的corefx需要注释,但大多数使用的API都是完全注释的。

空引用类型的路线图

当前,我们将完全可以为空的引用类型体验视为处于预览状态。它是稳定的,但是将这个特性广泛应用到到在我们自己的技术和更大的.NET生态系统中,需要一些时间来完成。

也就是说,我们鼓励库开发者现在就开始为他们的库做注解。这个特性只会随着更多的库采用空特性而变得更好,从而帮助.NET成为一个更加空-安全的语言。

在未来一年左右的时间里,我们将继续改进这个特性,并将其应用到整个Microsoft框架和库中。

对于该语言,特别是编译器分析,我们将进行大量的增强,以便尽可能减少您需要做的事情,如使用空-容错操作。其中许多增强功能已经在Roslyn上进行了跟踪。

对于corefx,我们将对剩下的大约80%的API进行注解,并根据反馈进行适当的调整。

对于ASP.NET Core和Entity Framework,我们将在添加了一些新的CoreFX 和编译器特性之后对公共API进行注解。

我们还没有计划如何注释WinForms和WPF APIs,但我们很高兴听到您对这些事情重要的反馈!

最后,我们将继续在Visual Studio中增强C#工具。我们对功能有多种想法来帮助使用该功能,但我们也希望您能提供宝贵意见!

下一步

如果您仍在阅读,并且没有尝试过在您的代码中使用这个功能,特别是您的库代码,就请尝试一下,并就您认为应该有所不同的内容向我们提供反馈。在.NET中使无法预料到的NullReferenceExceptions异常的消失就是一个漫长的过程,但我们希望从长远来看,开发人员不再需要担心被隐式的空值咬到。你可以帮助我们。尝试并开始注解您的库。对你的经验的反馈将有助于缩短这段旅程。

原文:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/

作者:DotNet Core圈圈
文章来自DotNet Core圈圈,版权归原作者,在转载时,请务必保留本版权声明和二维码。

分享.NET Core源码研究成果,并持续关注微服务、DevOps以及容器领域。愿我们共同努力,推动.NET生态的完善,促进.NET社区的进步

【译】尝试使用Nullable Reference Types的更多相关文章

  1. 初探 C# 8 的 Nullable Reference Types

    溫馨提醒:本文提及的 C# 8 新功能雖已通過提案,但不代表將來 C# 8 正式發布時一定會納入.這表示我這篇筆記有可能白寫了,也表示您不必急著瞭解這項新功能的所有細節,可能只要瞄一下底下的「概要」說 ...

  2. Nullable Reference Types 可空引用类型

    在写C#代码的时候,你可能经常会遇到这个错误: 但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作. 可空引用类型 Null Reference Type 所以, ...

  3. C# 8 - Nullable Reference Types 可空引用类型

    在写C#代码的时候,你可能经常会遇到这个错误: 但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作. 可空引用类型 Null Reference Type 所以, ...

  4. 迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)

    在我之前的一篇博客 NullReferenceException,就不应该存在! 中,我吐槽了 C# 中 null 的弊端以及避免 null 的方法:事实上这本都是现代高级语言中极力推崇的做法.Kot ...

  5. iOS: 学习笔记, 值与引用类型(译自: https://developer.apple.com/swift/blog/ Aug 15, 2014 Value and Reference Types)

    值和引用类型 Value and Reference Types 在Swift中,有两种数据类型. 一是"值类型"(value type), 它是每一个实例都保存有各自的数据,通常 ...

  6. 《javascript高级程序设计》第五章 reference types

    第5 章 引用类型5.1 Object 类型5.2 Array 类型 5.2.1 检测数组 5.2.2 转换方法 5.2.3 栈方法 5.2.4 队列方法 5.2.5 重排序方法 5.2.6 操作方法 ...

  7. swift语言点评十-Value and Reference Types

    结论:value是拷贝,Reference是引用 Value and Reference Types Types in Swift fall into one of two categories: f ...

  8. 快速了解C# 8.0中“可空引用类型(Nullable reference type)”语言特性

    Visual C# 8.0中引入了可空引用类型(Nullable reference type),通过编译器提供的强大功能,帮助开发人员尽可能地规避由空引用带来的代码问题.这里我大致介绍一下可空引用类 ...

  9. [译]AMQP 0-9-1 Quick Reference : basic

    Basic basic.ack(delivery-tag delivery-tag, bit multiple)Support: fullAcknowledge one or more message ...

随机推荐

  1. 机器学习中K-means聚类算法原理及C语言实现

    本人以前主要focus在传统音频的软件开发,接触到的算法主要是音频信号处理相关的,如各种编解码算法和回声消除算法等.最近切到语音识别上,接触到的算法就变成了各种机器学习算法,如GMM等.K-means ...

  2. Ural 2062:Ambitious Experiment(树状数组 || 分块)

    http://acm.timus.ru/problem.aspx?space=1&num=2062 题意:有n个数,有一个值,q个询问,有单点询问操作,也有对于区间[l,r]的每个数i,使得n ...

  3. ECharts 地图绘制与钻取简易接口

    1.地图绘制过程原理 给定范围边界经纬度数据,再给它个名字就构成了绘制地图的基础.也就是说,你可以绘制任意形状的地图版块. 2.地图数据生成 中国以及省市县等地图的基础数据可以从这里生成与下载. ht ...

  4. 一、Java语言概述与开发环境、第一个java程序

    目录: 1.1 Java特点 1.2 Java程序运行机制 1.3 安装JDl和配置环境变量 1.4 第一个JAVA程序 1.5 第一个JAVA程序的含义 前言 Java语言历时近二十年,已发展成为人 ...

  5. Linux命令学习-ps命令

    Linux中,ps命令的全称是process status,即进程状态的意思,主要作用是列出系统中当前正在运行的进程信息. ps命令的功能很强大,参数也非常多,下面只举几个简单的实例. 显示所有进程信 ...

  6. 几款常用的在线API管理工具(是时候抛弃office编写接口文档了)

    在项目开发过程中,总会涉及到接口文档的设计编写,之前使用的都是ms office工具,不够漂亮也不直观,变更频繁的话维护成本也更高,及时性也是大问题.基于这个背景,下面介绍几个常用的API管理工具,方 ...

  7. android_viewFlipper(一)

    需要注意的地方已在代码中表明 package cn.com.sxp; import android.app.Activity; import android.os.Bundle; import and ...

  8. 20141126-传智播客.NET3.3版

  9. Thread API的详细介绍

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurr ...

  10. 《ElasticSearch6.x实战教程》之分词

    第四章-分词 下雨天留客天留我不留 本打算先介绍"简单搜索",对ES的搜索有一个直观的感受.但在写的过程中发现分词无论如何都绕不过去.term查询,match查询都与分词息息相关, ...