1.4C#语言的类型(Type)

C # 语言有两种类型:值类型 和 引用类型。值类型的变量直接存储数据,而引用类型的变量则存储对数据(称为“对象”)的引用。

System.Object类(别名object) 是所有类型的最终基类。值类型 派生自 System.ValueType类 (派生自 System.Object类)。派生自 System.ValueType类 的类型在 CLR 中具有特殊行为。引用类型 直接派生自 System.Object类。

不可变类型(Immutable):值类型或引用类型的数据被初始化赋值后是不可变的,如果被修改需在新内存地址保存新值,旧值所在的旧内存地址随后会被垃圾回收。

可变类型(Mutable):值类型或引用类型的数据被初始化赋值后是可变的,如果被修改只需在原有内存地址将新值取代旧值。

值类型包括:结构(可变)、整数(不可变)、十进制浮点(不可变)、IEEE二进制浮点(不可变)、布尔(不可变)、Unicode 字符(不可变)、枚举(不可变)、元组值类型(可变)、Nullable<T>(不可变)。

引用类型包括:类(可变)、接口(可变)、Unicode 字符串(不可变)、数组(可变)、元组引用类型(不可变)、委托(不可变)。

.4C#语言的类型(Type)"/

1.结构(Struct)类型

别名说明默认值.NET API
struct格式为 struct S {...} 的用户自定义类型 —————— ——————

2.整数(Integral)类型

整数正常分为十进制(无任何前缀)、十六进制(0x 或 0X 前缀)、二进制(0b 或 0B 前缀)。

可以将数字分隔符(_)用于所有的整数类型。

类别别名位(Bit)范围/精度默认值.NET API
有符号整型sbyte8-128 … 1270System.SByte结构
short16-32768 … 327670System.Int16结构
int32-2,147,483,648 … 2,147,483,6470System.Int32结构
long(后缀L)64-9,223,372,036,854,775,808 … 9,223,372,036,854,775,8070System.Int64结构
nint32或64取决于平台0System.IntPtr结构
无符号整型byte80 … 2550System.Byte结构
ushort160 … 655350System.UInt16结构
uint(后缀U或u)320 … 4,294,967,2950System.UInt32结构
ulong(后缀U或u或L)640 … 18,446,744,073,709,551,6150System.UInt64结构
nuint32或64取决于平台0System.UIntPtr结构

3.十进制浮点(Decimal)类型

可以将数字分隔符(_)用于所有的浮点类型。

别名位(Bit)范围/精度默认值.NET API
decimal(后缀M或m)128±1.0 × 10-28到±7.9228162514264337593543950335 × 1028,大约 28-29 位数字0.0mSystem.Decimal结构

4.IEEE二进制浮点(Floating-point)类型

浮点类型遵循IEEE 754二进制浮点算术标准

浮点类型还包括三个特殊的值:NaN(非数字)、正无穷、负无穷。

可以将数字分隔符(_)用于所有的浮点类型。

别名位(Bit)范围/精度默认值.NET API
float(后缀F或f)32±1.401298 × 10−45到±3.40282347 × 1038,大约 6-9 位数字0.0fSystem.Single结构
double(不带后缀或后缀D或d)64±4.94065645841247 × 10-324到±1.7976931348623157 × 10308,大约 15-17 位数字0.0dSystem.Double结构

5.布尔(Boolean)类型

别名位(Bit)说明默认值.NET API
bool8布尔值 true 或 falsefalseSystem.Boolean结构

6.Unicode 字符(Char)类型

别名位(Bit)说明范围默认值.NET API
char16Unicode UTF-16 字符U+0000 到 U+FFFFU+0000或\0 System.Char结构
//以下都表示字符j

//字符自变量
'j'

//Unicode 转义序列
'\u006A'

//十六进制转义序列
'\x006A'

//字符代码的值转换为相应的char值
(char)106

对于 Unicode 转义序列,必须指定全部四位十六进制值。 也就是说,\u006A 是一个有效的转义序列,而 \u06A 和 \u6A 是无效的。

对于十六进制转义序列,可以省略前导零。 也就是说,\x006A\x06A 和 \x6A 转义序列是有效的,并且对应于同一个字符。

7.枚举(Enum)类型

别名说明默认值.NET API
enum格式为 enum E {...} 的用户自定义类型(E)0System.Enum

8.元组(ValueTuple)值类型

类型说明默认值.NET API
ValueTuple格式为 (T1, T2, ...) 的用户自定义类型,T为字段。——————System.ValueTuple结构

9.Nullable<T>值类型

类型说明默认值.NET API
Nullable<T>T类型 表示值必须是 T 类型的值,T?类型 表示值可以是 T类型的值 或 null 值。—————— System.Nullable<T>结构

10.类(Class)类型

别名说明默认值.NET API
class格式为 class C {...} 的用户自定义类型 —————— ——————

11.接口(Interface)类型

别名说明默认值.NET API
interface格式为 interface I {...} 的用户自定义类型 —————— ——————

12.Unicode 字符串(String)类型

string 类型表示零个或多个 Unicode 字符的序列。

别名说明默认值.NET API
string Unicode UTF-16 字符序列nullSystem.String

13.数组(Array)类型

别名说明默认值.NET API
array一维、多维和交错。 例如:int[]int[,] 和 int[][]nullSystem.Array

14.元组(Tuple)引用类型

类型说明默认值.NET API
Tuple格式为 (T1, T2, ...) 的用户自定义类型,T为属性。nullSystem.Tuple

15.委托(Delegate)类型

别名说明默认值.NET API
delegate格式为 delegate int D(...) 的用户自定义的类型nullSystem.Delegate

16.Nullable<T>引用类型

在C#8.0(不包含)之前,引用类型不支持T?形式的语法,T类型 表示值可以是 T类型的值 或 null 值。

在C#8.0(包含)之后,可以通过两种方式控制可为 null 的上下文:在项目配置文件中设置 和 在单个 C# 源文件中添加预处理器指令。在同时配置了两种方式的情况下,预处理器指令会覆盖项目配置文件中的设置。

当在启用可为 null 的上下文的情况下,T类型 表示值必须是 T 类型的值,T?类型 表示值可以是 T类型的值 或 null 值。

当在禁用可为 null 的上下文的情况下,引用类型不支持T?形式的语法,T类型 表示值可以是 T 类型的值 或 null 值。

注:警告的意思编译器会发出警告消息,批注的意思是在代码下面显式波浪线(“~~~~~”)批注。

在项目配置文件中设置可为 null 上下文
<Nullable>enable</Nullable>开启
<Nullable>disable</Nullable>禁用
<Nullable>warnings</Nullable>警告
<Nullable>annotations</Nullable>批注
在单个 C# 源文件中添加预处理器指令说明
#nullable enable将可为空批注和警告上下文设置为“已启用”。
#nullable disable将可为空批注和警告上下文设置为“已禁用”。
#nullable restore将可为空批注和警告上下文还原为项目配置文件。
#nullable enable annotations将可为空批注上下文设置为“已启用”。
#nullable disable annotations将可为空批注上下文设置为“已禁用”。
#nullable restore annotations将可为空批注上下文还原为项目配置文件。
#nullable enable warnings将可为空警告上下文设置为“已启用”。
#nullable disable warnings将可为空警告上下文设置为“已禁用”。
#nullable restore warnings将可为空警告上下文还原为项目配置文件。

17.Void返回类型

类型说明默认值.NET API
Void指定无返回值的方法的返回值类型——————System.Void结构

18.匿名类型

18.1创建匿名类型的对象

匿名类型提供了一种方便的方法,可以将一个或多个public只读属性封装到单个对象中,而无需先显式定义类型。类型名称由编译器生成。每个属性的类型由编译器推断。

//只使用对象初始值设定项创建匿名类型的对象。
var 变量 = new { 属性名1 = 值1, 属性名2 = 值2, 属性名N = 值N};
//调用属性
变量.属性名

//赋值运算符左侧指定属性名称,右侧为值。
var v = new { Amount = 108, Message = "Hello" };
Console.WriteLine(v.Amount + v.Message);

//赋值运算符左侧未指定属性名称,右侧为调用其它类型的属性,则编译器会给左侧指定与其它类型的属性名称相同的名称。
//匿名类型通常用在查询表达式的 select 子句中。
var productQuery =
    from prod in products
    select new { prod.Color, prod.Price };

foreach (var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

18.2匿名类型的类型转换

匿名类型是 class 类型,它们直接派生自 object,并且无法强制转换为除 object 外的任何类型。

18.3匿名类型的相同类型

如果程序集中的两个或多个匿名对象初始值设定项指定的属性具有相同的类型和名称且顺序相同,则编译器将对象视为相同类型的实例。它们共享同一编译器生成的类型信息。

18.4非破坏性变化(nondestructive mutation)

匿名类型支持 with 表达式形式的非破坏性变化,这使您能够创建一个匿名类型的新实例,其中一个或多个属性具有新值:

var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);

19.dynamic类型

dynamic 类型(翻译为:动态类型)是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查,会在运行时类型检查,相当于将静态类型转换为动态类型。

static void Main(string[] args)
{
    ExampleClass ec = new ExampleClass();
    // ec调用exampleMethod1会产生编译器错误,因为
exampleMethod1只定义了一个形参
    //ec.exampleMethod1(10, 4);

    dynamic dynamic_ec = new ExampleClass();
    // dynamic_ec调用exampleMethod1不会产生编译器错误,会产生运行时异常
    dynamic_ec.exampleMethod1(10, 4);

    // 无论方法是否存在,都不会引起编译器错误,会产生运行时异常

    dynamic_ec.someMethod("some argument", 7, null);
    dynamic_ec.nonexistentMethod();
}
class ExampleClass
{
    public ExampleClass() { }
    public ExampleClass(int v) { }

    public void exampleMethod1(int i) { }

    public void exampleMethod2(string str) { }
}

20.编译时类型和运行时类型

//编译时类型为 object,运行时类型为 string。
object anotherMessage = "This is another string of characters";

21.托管(Managed)类型和非托管(Unmanaged)类型

托管(Managed)类型:它是指受垃圾回收器管理的类型。

非托管(Unmanaged)类型:术语“非托管类型”有点误导人:它不是在非托管代码中定义的类型。它是指不受垃圾回收器管理的类型。

如果某个类型是以下类型之一,则它是非托管类型 :

  • 任何只包含非托管类型的字段的用户自定义的封闭式或开放式构造结构类型。
  • sbytebyteshortushortintuintlongulongfloatdoubledecimal 、boolchar
  • 任何枚举类型
  • 任何指针类型

从 C# 7.3 开始,可使用 unmanaged 约束定义一个“类型参数为非指针、非Nullable”的非托管类型。

22.类型转换

隐式转换

隐式转换始终会成功且不会导致数据丢失,因此无需使用任何特殊语法。

//从范围较小的整数值类型到范围较大的整数值类型的隐式转换
int num = 2147483647;
long bigNum = num;
//从派生类引用类型到直接或间接的基类或接口引用类型的隐式转换
Derived d = new Derived();

Base b = d;

显式转换(强制转换)

显式转换,又称强制转换,可能会导致数据丢失,因此必须使用强制转换表达式

//从精度较高或范围较大的数值值类型到精度较低或范围较小的数值值类型的显式转换
double x = 1234.7;
int a;
a = (int)x;
System.Console.WriteLine(a);
// Output: 1234
//从基类引用类型到派生类引用类型的显式转换

//隐式转换到基类是安全的
Giraffe g = new Giraffe();
Animal a = g;
//显式转换到派生类型,如果右侧的a实际不是长颈鹿类型,将在运行时抛出异常
Giraffe g2 = (Giraffe)a;

23.装箱(Boxing)和取消装箱(Unboxing)

装箱

装箱是将值类型隐式或显式转换为 object 类型 或 由此值类型实现的任何接口类型。

int i = 123;
// 隐式装箱i
object o = i;
// 显式装箱i
object o = (object)i;

取消装箱

取消装箱是从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换。

// 显式取消装箱
int j = (int)o;

24.协变(Covariance)和逆变(Contravariance)

例如:Cat类型 是 Animal类型的子类型。

协变(covariant):因为IEnumerable<T>中的T是协变的,所以子类型关系被保持(preserved),IEnumerable<Animal> = IEnumerable<Cat>

逆变(contravariant):因为Action<T>中的T是逆变的,所以子类型关系被反转(reversed),Action<Cat> = Action<Animal>

不变(invariant):因为IList<T>中的T是不变的,所以子类型关系被忽略(ignored),无论 IList<Cat> 还是 IList<Animal>都不可以赋值给对方。

协变和逆变能够实现 数组类型委托类型类型参数、泛型类型参数 的引用类型的隐式转换。

协变

//数组类型
object[] array = new String[10];
//委托类型类型参数(方法返回类型)
Func<T>
//泛型类型参数(方法返回类型)
IEnumerable<Object> listObjects = new List<String>();
//使用 out 关键字将委托类型类型参数(方法返回类型)声明为协变
public delegate R DCovariant<out R>();
//使用 out 关键字将泛型类型参数(方法返回类型)声明为协变
interface ICovariant<out R>
{
    R GetSomething();
}

逆变

//委托类型类型参数(方法参数类型)
Action<T>
//使用 in 关键字将委托类型类型参数(方法参数类型)声明为逆变
public delegate void DContravariant<in A>(A a);
//使用 in 关键字将泛型类型参数(方法参数类型)声明为逆变
interface IContravariant<in A>
{
    void SetSomething(A sampleArg);
}

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/7858.html

(0)
上一篇 2021年1月4日 02:33
下一篇 2021年1月5日 02:35

相关推荐

  • 3.15C#语言的可访问性(Accessibility)

    访问修饰符 程序集 是通过在单个编译中编译一个或多个 .cs 文件而创建的 .dll 或 .exe。 调用方的位置 public protected internal protected internal private protected private 在类内 ✔️️ ✔️ ✔️ ✔️ ✔️ ✔️ 派生类(相同程序…

    C#语言教程 2021年3月15日
    02940
  • 3.4C#引用类型之类(Class)

    1.类声明 2.继承 派生类只能有一个直接基类。但是,因为一个基类本身可能继承自另一个类,所以一个类可能会间接继承多个基类。省略基类相当于从 object 类型继承。 派生类会隐式获取基类的所有成员(除了基类的静态构造函数、实例构造函数、析构函数),所以无需在派生类再书写继承过来的基类成员,但您还可…

    C#语言教程 2021年3月4日
    02970
  • 2.7C#函数成员之事件(Event)

    1.事件委托声明 .NET 类库中的所有事件均基于 EventHandler 委托、EventHandler<TEventArgs> 委托,一般不建议自定义委托。 第一个参数为object类型,表示能够引发事件的类的实例。 第二个参数为从 EventArgs 基类派生的子类型的…

    C#语言教程 2021年2月7日
    02220

发表评论

登录后才能评论