操作符(Operator)
C#的操作符是一种告诉编译器执行计算、逻辑判断的符号。
default(x)
获取类型的默认值,x是类型。虽然可以为任意类型使用此操作符,但此操作符主要用于泛型,在不确定泛型具体的类型时使用default(T)以便得到该泛型的默认值。
checked(x)
栈溢出检查,当编译器不能检查到溢出时(系统默认是unchecked,即不检查溢出),可通过显示调用checked,x是数值,如果不溢出就返回与数值相同的类型,否则抛出异常。
Console.WriteLine(x);//print 4294967295
try
{
uint z = checked(x + 1);//检查是否栈溢出
}
catch
{
Console.WriteLine("栈溢出");
}
//另一种写法
uint x = uint.MaxValue;
Console.WriteLine(x);//print 4294967295
checked
{
try
{
uint z = x + 1;
Console.WriteLine(z);
}
catch
{
Console.WriteLine("栈溢出");
}
}
sizeof(x)
获取字节数,只能获取值类型(结构)在内存中所占的字节数并返回一个int的字节值。
Console.WriteLine(i);
如果是自定义结构,则需要依次点击项目-当前项目属性-生成-勾选允许不安全代码。
->(不安全指针)
操作指针,直接操作内存修改数据,只能作用于值类型数据。
{
public int Score;
};
Student stu;
stu.Score = 99;
unsafe
{
Student* StuPointer = &stu;
StuPointer->Score = 100;
Console.WriteLine(stu.Score);//直接修改数据
}
!x
取非,x是布尔值,通常用在一个返回bool值的函数上,如果该函数的逻辑是:是否不是,则取非后描述为:是否是。如果函数的逻辑是:是否是,则描述为是否不是。
//IsNullOrEmpty()方法测试是否是,取非则变成是否不是,不是则执行
if (!string.IsNullOrEmpty(s))
{
//……
}
x++
后置++,该数加1并赋给该变量,x++即x=x+1
x--
后置--,该数减1并赋给该变量,x--即x=x-1
++x
前置++,该数加1并赋给该变量,++x即x=x+1
--x
前置--,该数减1并赋给该变量,--x即x=x-1。以上4个操作符相同处在于都是自加1或自减1。差异在于,前置会参与其它赋值运算,后置不会。
//a++ 相当于a=a+1
//++a相当于a=a+1
//int z=++a 则z=2 a=2,int y=a++则y=1 a=2
int x = 5;
x = x--;
Console.WriteLine(x);//return 5 因为x--不参与赋值
x = --x;
Console.WriteLine(x);//return 4
%(取模)
返回两数相除的余数。
-x(取相反数)
取相反数,唯一例外:带符号的值类型的minValue(负数)使用此操作符后结果不变。
Console.WriteLine(x);
Console.WriteLine(-x);//print 2147483647
int x = int.MinValue;
Console.WriteLine(x); //print -2147483648
Console.WriteLine(-x);//print -2147483648
~x(按位求反)
取x的二进制数中每一个位的相反数,经测试,结果规律就是十进制数的结果+1,再在结果上取相反数。
Console.WriteLine(x); //print 111
Console.WriteLine(~x);//print -112
int x = int.MinValue;
Console.WriteLine(x); //print -2147483648
Console.WriteLine(~x);//print 2147483647
List<string> list = new List<string> { "sam", "leo" };
int result = list.BinarySearch("korn");
if (result < 0)
{
list.Insert(~result, "korn");
}
Console.WriteLine(list.BinarySearch("korn")); //print 0
x<<N(左位移)
位移操作符的计算是将操作数的二进制数向左移动N位,x是操作数,N是位移量。如果N=0,则不发生位移。注意溢出。这比使用两个操作数进行数学运算快得多,也节省内存资源,因为是直接在一个操作数上进行位移得出结果。位移操作符通常用于位标志枚举,将从一个数上进行位移得到的结果作为枚举值来使用。
int y = x << 2;
x的二进制数补满32位后如图:
32位的数整体向左移两位后,后面会差二位,将以0填充补满32个位。
十进制数的左位移计算公式:每移动1位,x*2
如果是负数的左位移,同样也是这样算,如果结果有小数,则去掉小数(不会四舍五入)。
int y = x <<3;
Console.WriteLine(y); //print 80
x>>y(右位移)
将操作数的二进制数向右移动N位,x是操作数,N是位移量。如果N=0,则不发生位移。注意溢出。
int y = x >> 2;
x的二进制数补满32位后如图:
32位的数整体向右移两位后,前面会差两位,将以0填充补满32个位。
十进制数的右位移计算公式:每移动1位,x/2
如果是负数的右位移,同样也是这样算,如果结果有小数,则去掉小数再进一。
int y = x >> 2;
Console.WriteLine ( "x的二进制数据为: " + Convert.ToString ( x , 2 ).PadLeft ( 32 , '0' ) );
Console.WriteLine ( "x向右移2位后的二进制数据为:" + Convert.ToString ( y , 2 ).PadLeft ( 32 , '0' ) );
& (位与)
位与,将两个操作数的二进制数的每个位对齐后按两个数所对应的位进行判断,只要其中一组的两个数对应的位都是1,结果就为1,否则为0,最终得到一个结果二进制数。
int y = 28;
int z = x & y; //z=4
//x和y的二进制表示
string xBinary = Convert.ToString(x, 2).PadLeft(32, '0');
string yBinary = Convert.ToString(y, 2).PadLeft(32, '0');
string zBinary = Convert.ToString(z, 2).PadLeft(32, '0');
Console.WriteLine(xBinary);
Console.WriteLine(yBinary);
Console.WriteLine(zBinary); //z的二进制数是100
结果二进制数为00000000000000000000000000000100,十进制为4。
使用场景:
if ((i & 1) != i)
{
Console.WriteLine("是偶数");
}
∣(位或)
将两个操作数的二进制数的每个位对齐后按两个数所对应的位进行判断,只要其中一组的两个数对应的位只要有一个是1结果为1,,否则为0
使用场景:
if ((i | 1) != i)
{
Console.WriteLine("是偶数");
}
^
位抑或,将两个操作数的二进制数的每个位对齐后按两个数所对应的位进行判断,只要其中一组的两个数对应的位不同时,结果为1,否则为0
is
检查左边的对象是否兼容右边指定的类型,并返回一个bool值
Console.WriteLine ( x is null ); //null表示空引用,不指向内存中的任何地址
&&
条件与,都为真时,返回真。一遇到假,后面就不再计算并立即返回假。
bool b = 5 > 0 && 5 >= 0 && 5 != 0;
int c = 5;
if (c > 8 && c++ > 1) { }
Console.WriteLine(c);//print 5 c>8为假,后面不再计算,所以c=5。
||
条件或,只要有一个为真,返回真,遇到真,后面就不再计算并立即返回真。
bool b = 5 > 0 || 5 == 0 || 5 < 0;
T?
声明可空类型的变量,声明一个可以为null的值类型变量,T是值类型。
a = null;
bool b = a.HasValue;//测试a是否有值
//或
int? a = 100;
a = null;
bool b = a.HasValue;//测试a是否有值
x??y
空检测,如果x是null,则取值y,否则取值x
int y = a ?? 100;
Console.WriteLine(y);//print 100
public class A { }
A a = null;
A aNew = a ?? new A();
Console.WriteLine(aNew.GetType().Name);//print A
//三元操作符和空操作符的相似性
string sam = null;
string leo = null;
string korn = "korn";
DisplayName = sam ?? (leo ?? korn); //sam是空则把(leo??korn)返回,如果leo是空则把korn返回
DisplayName = !string.IsNullOrEmpty(sam) ? sam : !string.IsNullOrEmpty(leo) ? leo : korn;
a=x?y:z
条件测试,简化if……else语句,如果x表达式为真,y赋给a,否则z赋给a。a是变量,x是表达式。
int y = x == 1 ? x + 1 : x == 2 ? x + 2 : x + 0;
//等同于
if (x == 1) y = x + 1;
else if (x == 2) y = x + 2;
else y = x + 0;
注意,如果a是var隐式类型,则它和其它确定的类型一样:y和z必须返回同一种数据类型。
=、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
一个变量是否被赋值就看它后面是否使用了赋值操作符,即使赋值为null也算是赋了值,即也是被初始化了的变量。
int r = 100;
r += z;//即r=r+z
int a = 1, b = 2, c = 3, d = 4, e = 5;
a += b += c += d += e;//1+2+3+4+5
Console.WriteLine ( a );//print 15
int x = 100;
int y = 200;
int z = x + y;//从右至左,算出x+y再赋给z
x += y += z;//y=y+z,算出y的值再算x=x+y
@(标识符)
@标识符有以下用途
1.在字符串前使用@表示该字符串原样输出,无需转义
2.在C#预定义的关键字前使用@表示非关键字,比如你可能想定义一些与C#关键字同名的变量,可以使用@标识符,如:
{
public string @bool;
}
面试遇到这种题:
//0+1 = 1;
//y+z=q
//1+1=2
//q+z=z
//2+1=3
//z+q=q
//推出重复公式
//q+z=z(奇数次时)
//z+q=q(偶数次时)
for (int x = 0, y = 0, z = 1, q = 0; x < 30; x++)
{
if (x == 0)
{
q = y + z;
Console.WriteLine( q );
}
else
{
if ((x | 1) != x)
{
q = q + z;
Console.WriteLine( q );
}
else
{
z = z + q;
Console.WriteLine( z );
}
}
}