值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。
值类型,声明一个值类型的时候,是在“栈”中开辟一个内存空间来存放对应的值,当值类型的值发生改变的时候,则直接修改该内存空间所保存的值
。
引用类型,声明一个引用类型的时候,首先是在“堆”中开辟一个内存空间来存放对应的值,然后在“栈”中开辟一个内存空间用于保存在“堆”中开辟的内存空间的地址。当系统调用引用类型的时候,首先去“栈”中获取到地址,然后根据地址在“堆”中找到对应的内存空间来获取到对应值。像数组这样的引用类型
从内存上看,值类型是在栈中的操作,而引用类型是在堆中的操作
。(导致 => 值类型存取速度快,引用类型存取速度慢。)
从本质上看,值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用
。(值类型是具体的那个数值所占用的空间大小,而引用类型是存放那个数值的空间地址。)
从来源上看,值类型继承自System.ValueType,引用类型继承自System.Object。
特别的:结构体是值类型,类和string是引用类型。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
装箱:值类型转换为对象类型, 实例:
int val = 8;
object obj = val;//整型数据转换为了对象类型(装箱)
拆箱:之前由值类型转换而来的对象类型再转回值类型, 实例:
int val = 8;
object obj = val;//先装箱
int nval = (int)obj;//再拆箱
只有装过箱的数据才能拆箱
就像仓库,仓库里有货架,货架上有编号:A1,A2,A3…, 这些编号就可以看做是引用类型,现在来了一批货,有 “土豆,黄瓜,西红柿”,这些就是值类型,如果你想让 A1=土豆,那么就要把土豆搬到 A1 里面去,这就叫装箱,装箱需要耗费人力和工时(也就是耗费CPU和内存),同理拆箱就要把对应编号的货物搬出来,也是需要耗费人力和工时。
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:
dynamic <variable_name> = value;
例如:
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
@引号---->有点类似于Python中的 ----> r引号
例如:
String str = "runoob.com";
一个 @引号字符串:
@"runoob.com";
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"C:\Windows";
等价于:
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"";
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
type* identifier;
例如:
char* cptr;
int* iptr;
类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:
double d = 5673.74;
int i;
// 强制转换 double 为 int
i = (int)d;
python中的写法是:
i = int(d)
隐式转换:C# 默认的以安全方式进行的转换。本质是从小存储容量数据类型自动转换为大存储容量数据类型,从派生类转换为基类。
显式转换:通过用户使用预定义的函数显式完成的,显式转换需要强制转换运算符。转换类型的范围大小和从属关系和隐式转换相反。显式转换可能会导致数据出错,或者转换失败,甚至无法编译成功。
没搞清楚 Convert.ToInt32 和 int.Parse() 的细细微区别时千万别乱用,否则可能会产生无法预料的结果,举例来说:假如从 url 中取一个参数 page 的值,我们知道这个值是一个 int,所以即可以用 Convert.ToInt32(Request.QueryString[“page”]),也可以用 int.Parse(Request.QueryString[“page”]),但是如果 page 这个参数在 url 中不存在,那么前者将返回 0,0 可能是一个有效的值,所以你不知道 url 中原来根本就没有这个参数而继续进行下一下的处理,这就可能产生意想不到的效果,而用后一种办法的话没有 page 这个参数会抛出异常,我们可以捕获异常然后再做相应的处理,比如提示用户缺少参数,而不是把参数值当做 0 来处理。
(1) 这两个方法的最大不同是它们对 null 值的处理方法: Convert.ToInt32(null) 会返回 0 而不会产生任何异常,但 int.Parse(null) 则会产生异常。
(2) 对数据进行四舍五入时候的区别
a. Convert.ToInt32(double value) 如果 value 为两个整数中间的数字,则返回二者中的偶数;
即 3.5 转换为 4,4.5 转换为 4,而 5.5 转换为 6。不过 4.6 可以转换为 5,4.4 转换为 4 。
b. int.Parse("4.5") 直接报错:"输入字符串的格式不正确"。
c. int(4.6) = 4 Int 转化其他数值类型为 Int 时没有四舍五入,强制转换。
(3) 对被转换类型的区别 int.Parse 是转换 String 为 int, Convert.ToInt32 是转换继承自 Object 的对象为 int 的(可以有很多其它类型的数据)。你得到一个 object 对象, 你想把它转换为 int, 用 int.Parse 就不可以, 要用 Convert.ToInt32。
C#
例1:
int i, j, k;
char c, ch;
float f, salary;
double d;
例2:---->数组
double[] balance;
初始化数组
double[] balance = new double[10];
赋值给数组
double[] balance = new double[10];
balance[0] = 4500.0;
您可以在声明数组的同时给数组赋值,比如:
double[] balance = { 2340.0, 4523.69, 3421.0};
您也可以创建并初始化一个数组,比如:
int [] marks = new int[5] { 99, 98, 92, 97, 95};
在上述情况下,你也可以省略数组的大小,比如:
int [] marks = new int[] { 99, 98, 92, 97, 95};
您也可以赋值一个数组变量到另一个目标数组变量中。在这种情况下,目标和源会指向相同的内存位置:
int [] marks = new int[] { 99, 98, 92, 97, 95};
int[] score = marks;
二维数组
C#中,我们在创建二维数组的时候,一般使用arr[][]的形式,例如
int[][] aInt = new int[2][];
但声明二维数组还有一种方法,是使用arr[,]的形式。两者有什么区别呢?
实际上,形如arr[,]只能声明等长的二维数组,例如
int[,] ab1 = new int [2,3];//默认值为0; int[,] ab2 = new int[2,3]{{1,2,3},{4,5,6}};
形如arr[][]的形式则可以声明等长二维数组,也可以声明不等长二维数组。例如
int [][] abc = new int[2][]; abc[0] = new int[]{1,2}; abc[1] = new int[]{3,4,5,6};
python
例1:
n=1
b=2
s= 'lalala'
例2:
list01 = []
list02 = [1,2,3,4,5,6]
list03 = [i for i in range(18)]
list04 = list('abcd')
二维数组 例如3X3数组
list05 = [
[1,2,3],
[4,5,6],
[7,8,9],
]
例3:
dict01 = {}
dict02 = {'name':'Amy','age':18}
算数运算符 | + - * /、%、 ++、 - - |
---|---|
关系运算符 | < 、> 、<=、>=、==、!= |
逻辑运算符 | && 与、||或、!非 |
位运算符 | &与、|或、^非、~取反、<<左移、>>右移 |
赋值运算符 | += 、-= 、*=、/=、%=、&=按位与、|=按位或、^=按位非、<<=按位左移、>>=按位右移 |
其他运算符 | &取地址、*指针、? :条件表达式、is、as、sizeof()、typeof() |
?:
条件表达式
例如:
x?y:z 表示如果表达式 x 为 true,则返回 y;如果 x 为 false,则返回 z,
是 if{}else{} 的简单形式。
as
强制转换,即使转换失败也不会抛出异常。
Object obj = new StringReader("Hello");
StringReader r = obj as StringReader;
引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空。
例如:string str=null; 是正确的,int i=null; 编译器就会报错。
为了使值类型也可为空,就可以使用可空类型,即用可空类型修饰符 ? 来表示,表现形式为 T? 。
例如:int? 表示可空的整形,DateTime? 表示可为空的时间。
T? 其实是 System.Nullable(泛型结构)的缩写形式,也就意味着当你用到 T?时编译器编译时会把T?编译成 System.Nullable 的形式。
例如:int?,编译后便是 System.Nullable 的形式。
例如:x?y:z 表示如果表达式 x 为 true,则返回 y;如果 x 为 false,则返回 z,
是 if{}else{} 的简单形式。
用于定义可空类型和引用类型的默认值。
如果此运算符的左操作数不为 null,则此运算符将返回左操作数,否则返回右操作数。
例如:a??b 当 a 为 null 时则返回 b,a 不为 null 时则返回 a 本身。
空合并运算符为右结合运算符,即操作时从右向左进行组合的。
如: a??b??c 的形式按 a??(b??c) 计算。
int? firstX = points?.FirstOrDefault()?.X;
从这个例子中我们也可以看出它的基本用法:如果对象为 NULL,则不进行后面的获取成员的运算,直接返回 NULL。
需要注意的是,由于 ?. 运算符返回的可以是 NULL,当返回的成员类型是 struct 类型的时候, ?. 和 . 运算符的返回值类型是不一样的。
Point p = new Point(3, 2);
Console.WriteLine(p.X.GetType() == typeof(int)); //true
Console.WriteLine(p?.X.GetType() == typeof(int?)); //true
例1:
c = a++: 先将 a 赋值给 c,再对 a 进行自增运算。
c = ++a: 先将 a 进行自增运算,再将 a 赋值给 c 。