C#基础操作符详解

本节内容:

1.操作符概览;

2.操作符的本质;

3.操作符与运算顺序

4.操作符详解。

 

1.操作符概览:

 

 

 

操作符(Operator)也译为”运算符”

操作符是用来操作数据的,被操作符操作的数据称为操作数(Operand)

表格从上往下优先级递减,同一行运算符的优先级一样一般按从左到右算,

“=”赋值操作符,是先运算右边的值再运算左边的值,所以是最后运算的。

 

2.操作符的本质

①操作符的本质是函数(即算法)的”简记法”

假如没有发明”+”只有Add函数,算式3+4+5将可以写成Add(Add(3,4),5)

假设没有发明”*”只有Mul函数,那么算式3+4*5将只能写成Add(3,Mul(4,5))

可见有操作符可读性更强。

②操作符不能脱离与它关联的数据类型(比如double数据类型的除法与int类型的除法相同数据结果不同)

可以说操作符就是与固定数据相关联的一套基本算法的简记法。

示例:为自定义的数据类型创建操作符。(格式为把方法名字改为”operator  想要定义的操作符”如:”operator +”)如下例子进一步说明了C#里面的操作符就是方法,也就是函数的一个简记法。

 

复制代码

复制代码

    class Person{public string Name;//public static List<Person>GetMary(Person p1, Person p2)(一般方法自定义操作符之前)public static List<Person>operator +(Person p1, Person p2){List<Person> people = new List<Person>();people.Add(p1);people.Add(p2);for (int i = 0; i < 11; i++){Person child = new Person();child.Name = p1.Name + "&" + p2.Name + "'s child";people.Add(child);}return people;}}

复制代码

 

 

 

 

3.操作符与运算顺序

①操作符的优先级

可以使用圆括号提高被括起来的表达式的优先级。

圆括号可以嵌套。

不像数学里面有方括号和花括号,在C#语法中”[]”与”{}”有专门的用途。

②同优先级操作符的运算顺序

除了带有赋值功能的操作符,同优先级操作符都是有左到右进行运算,

带有赋值功能的操作符的运算顺序是由右到左(比如赋值运算符”=”),

与数学运算不同,计算机语言的同优先级运算没有”结合率”:

3+4+5只能理解为Add(Add(3,4),5)不能理解为Add(3,Add(4,5)。

 

  1. 操作符详解

4.1基本操作符

①(成员访问操作符)”.”操作符(上表中写为X.Y):四种功能;

*访问命名空间当中的子集命名空间;

*访问名称空间当中的类型;

*访问类型的静态成员(静态成员隶属于类本身故用类可以访问,而用类的对象不能访问类的静态成员);

*访问对象的成员(包括数据成员和方法);

②方法调用操作符”()”即方法后面跟着的那对圆括号(上表写为f(x))。

调用方法一定要加圆括号,但是:

Action myAction = new Action(c.PrintHello);//把PrintHello方法交给委托对象myAction管理

  myAction();//这样在委托对象后面加个圆括号就相当于调用了被它管理的方法了,

这个时候PrintHello方法可以不带圆括号。

③元素访问操作符”[]”

   int[] myIntArray = new int[13];//创建int数组的实例13个元素

   int[] myIntArray2 = new int[]{1,2,3,4,5};//也可以在后面加花括号输入相应值,这对或括号叫做"初始化器",[]里不写数组大小会根据初始化器自动赋值...

   myIntArray[0]=2;//访问的是第一个数组元素,访问数组元素,[]里写的是偏移量,从0开始。

 

Dictionary<string, Student> stuDic = new Dictionary<string, Student>();//一个类名后面,跟着一个尖括号表示这个类是泛型

            //泛型是不完整的类如Dictionary<string, Student>在尖括号里要说明索引的类型(string)与值的类型(Student)(顺带一提Dictionary是一个字典类型)

 

*总结元素访问操作符”[]”里面放的是索引里面不一定是整数,如以下举例。

复制代码

复制代码

 1     class Program2     {3         static void Main(string[] args)4         {5             Dictionary<string, Student> stuDic = new Dictionary<string, Student>();//一个类名后面,跟着一个尖括号表示这个类是泛型6             //泛型是不完整的类如Dictionary<string, Student>在尖括号里要说明索引的类型(string)与值的类型(Student)(顺带一提Dictionary是一个字典类型)7             for (int i = 1; i < 100; i++)8             {9                 Student stu = new Student();
10                 stu.Name = "s_" + i.ToString();
11                 stu.Score = 100+i;
12                 stuDic.Add(stu.Name, stu);//把stu放进字典里面,所以为stu.Name,值为stu
13             }
14             Student number6 = stuDic["s_6"];//说明了[]里不一定是整数,而一定是索引
15             Console.WriteLine(number6.Score);
16         }
17     }
18     class Student
19     {
20         public string Name;
21         public int Score;
22     }

复制代码

 

④x--与x++:叫做后置的加加和后置的减减:

Int x=100; int y=x++;结果为x=101;y=100;因为x++是先赋值再进行自增;

--x与++x:叫做前置的加加和前置的减减:先进行自增或自减后进行赋值。

 

⑤typeof()操作符和default()操作符

*typeof操作符的作用为查看变量的种类:

Type t = typeof(int);

            Console.WriteLine(t.Namespace);

            Console.WriteLine(t.FullName);

            Console.WriteLine(t.Name);

*Default操作符使操作数取默认值:数值型为0,引用型为null,

  int x=default(int);//default操作的类型为结构体类型即数值类型时就返回内存块当中为0的值:

  Console.WriteLine(x);

输出为0;

  Form myForm = default(Form);//default操作数的类型为引用类型时就返回内存块当中为0的值即为null

  Console.WriteLine(myForm==null);

输出为true;

当为枚举型enum时: Level level=default(Level);

             Console.WriteLine(level);

  enum Level

    {

        Mid,

        Low,

        High

}

结果为Mid,如果把Mid的位置和Low互换则结果为Low,这是因为当default操作符遇到枚举类型会把它当做数值型来处理,即第一个元素为0,后面的依次+1;

如果这样写:

enum Level

    {

        Mid=1,

        Low=0,

        High=2

}则返回值为Low。当用default获取枚举值的时候要小心,如果这样写:

enum Level

    {

        Mid=1,

        Low=3,

        High=2

}返回值为0,出错了,所以在设置枚举值时最好给元素一个0的整数值。

 

先说明:关键字var:帮助生成隐式类型变量:

 int x;//显式变量,明确的告诉了编译器x属于什么数据类型;

         var y;//隐式变量,告诉编译器y的类型暂时不知道,当我赋值的时候看着办

C#是强类型语言变量一旦确定数据类型就不可以变更。

⑥new操作符:

*帮助我们在内存当中创建一个类型的实例并且立刻调用这个实例的实例构造器(所谓的构造函数),并取得的实例地址....

  new Form();//调用默认实例构造器

创建这个实例之后如果没有任何变量去引用它,访问它,过一会垃圾收集就把这个实例所占用的堆内存当做垃圾给收回来了。

*除了创建实例和调用实例构造器之外还能把new取得的实例地址通过赋值符号交给负责访问这个实例的变量。这样就在变量和实例之间构成了引用关系。有了这个引用关系之后就可以通过这个变量来访问实例。如: Form myForm=new Form();//调用默认实例构造器

  myForm.Text = "Hello!";//通过变量来访问实例

 

*上面为主要功能,以下为附加功能:调用实例的初始化器:

  Form myForm = new Form() {Text="Hello!" };在实例后面加花括号里面加属性的值。

可以初始化多个属性,中间逗号隔开。

还有:有的时候用实例只是一次性的没必要创建一个引用变量去初始化它,可以采用这时初始化器就发挥作用了:new Form(){Text=”Hello!”}.ShowDialog();只是由于没有引用变量引用(没有小孩牵着这个气球,气球一会就飞走了)所以一段时间后,垃圾回收器把它的堆内存回收。

a、错觉:要创建类的实例就一定要使用new操作符,错误的。如string Name = "Hello!";

String是一个类,创建实例时不用new操作符,这种方式叫做C#的”语法糖衣”,原因为为了统一使string与int的书写格式,而把string类的new操作符隐藏起来了,string可以用new但平常不这么用。类似的还有数组:

用new操作符:

int[] myArray = new int[10];//由于int的实例构造器有点特殊不用圆括号调用;

不用new操作符时:

 int[] myArray = { 1,2,3,4};

b、new操作符特殊用法:为匿名类型创建实例,

Form myForm=new Form(){Text=”Hello!”};当为非匿名类型创建实例时new后面要加类型名,

当为匿名类型创建实例时:如:

Var person=new {Name=”Mr li”,Age=34};//new操作符后面不跟类型,直接用初始化器初始化实例,什么类型?让编译器根据初始化内容自行判断,不过该实例一定要有引用变量引用,不知道类型?用var隐式变量即可。那到底是什么类型呢?

Console.WriteLine(Person.GetType().Name);

输出为:<>f__AnonymousType0`2

“<>f__AnonymousType”为约定的前缀,0表示我在程序中创建的第一个,’2表示这个类型为泛型类,构成这个类型的时候你需要两个类型来构成它,哪两个类型呢?就是初始化器里面的一个是string,一个是int。这是在创建匿名类型时编译器自己识别的类型。

这里才真正体现出var类型(全部)功能的强大之处与重要性。因为如上一种情况就算你想写出它的类型也不知道叫什么名字。

*记住new操作符与var隐式变量组合的使用方法:是为匿名对象创建对象并且用隐式类型变量来引用这个实例。

c、new操作符有危险性(功能强大伴随的滥用风险)一旦在某个类里面(比如main函数隶属的Program类)用new操作符创建了某个类的实例(比如在main函数中创建Form类),那么这个类(Form)就与主类(Program)紧紧耦合在一起,Pragram类就紧紧依赖于Form类,一旦某个类(Form)出现问题,整个耦合体都无法正常运行。即new操作符会造成紧耦合。那怎么解决?在软件工程有项非常重要和实用的技术叫做”设计模式”,在”设计模式”当中有一种非常重要的模式叫做”依赖注入”(dependenty injection),该模式就是帮助我们把紧耦合变成相对松的耦合。有概念即可:new操作符有风险慎用,大型程序中为了避免有紧耦合的情况我们有一种叫做”依赖注入”的设计模式可以使用,实现不必关注。

*程序设计追求”高内聚低耦合”

d、new关键字的多用性(不是操作符而是关键字):如

class Student

    {

        public void Report()

        {

            Console.WriteLine("I'm a student");

        }

    }

    class CsStudent:Student

    {

        new public void Report()//这叫子类对父类方法的隐藏,这里的new便不是操作符而是修饰符用来修饰new后面的方法的。(并不常见)

        {

            Console.WriteLine("I'm a Cstudent");

        }

}

则 Student stu = new Student();

            stu.Report();

            CsStudent csStu = new CsStudent();

            csStu.Report();时分别调用各自的Report()方法。

 

⑦checked()和unchecked()操作符:用来检查()内的值在内存中是否有溢出:(Overflow)

C#是强类型语言,任何一个变量它在内存里面都有数据类型,而数据类型有个非常重要的作用就是表示这种数据类型的实例在内存当中能够占多大的空间,一个值在内存空间所占的大小决定了这个值能够表达的范围,一旦超出这个范围这个值就产生了溢出。Checked就是告诉我们要去检出溢出,unchecked则告诉我们不用:

 uint x = uint.MaxValue;

            Console.WriteLine(x);

            string binStr = Convert.ToString(x, 2);

            Console.WriteLine(binStr);

            try

            {

                uint y = checked(x + 1);//检测x+1是否溢出,溢出后去catch捕获异常

                Console.WriteLine(y);

            }

            catch (OverflowException ex)

            {

                Console.WriteLine("There is overflow");

            }

Unchecked()操作符表示不用检查,C#中默认该种方式。Checked也有其他用法:

Checked

{

  try

            {

                uint y = checked(x + 1);//检测x+1是否溢出,溢出后去catch捕获异常

                Console.WriteLine(y);

            }

            catch (OverflowException ex)

            {

                Console.WriteLine("There is overflow");

            }

}

直接判断整个语句块中所有语句是否有溢出。

 

⑧delegate操作符(关键字)最主要的作用为声明一种叫委托的数据类型,委托是C#非常重要的概念。本节主要讲其作为操作符的作用(非常稀有因为拉姆达表达式(Lambda Expressions)的出现就是来替代delegate当做操作符的场景):使用delegate生成匿名方法:

 this.myButton.Click +=delegate (object sender, RoutedEventArgs e)//使用delegate声明了一个匿名方法

        {

            this.myTextBox.Text = "Hello World!";

        };

程序原本应为:this.myButton.Click += myButton_Click;

 void myButton_Click(object sender, RoutedEventArgs e)

        {

            this.myTextBox.Text = "Hello World!";

        }

现在替代这用用法的拉姆达表达式:

 this.myButton.Click += (sender,  e)=>

        {

            this.myTextBox.Text = "Hello World!";

        };

语法的演变可见C#语法越来越简洁,功能越来越强大。

 

⑨sizeof()操作符:

a、只能获取结构体类型在内存中所占字节数,默认情况下:sizeof只能去获取基本数据类型他们的实例在内存当中所占的字节数,基本数据类型:比如int、uint...说白了就是C#关键字里面那些除了string和object的数据类型:因为这两个为引用类。

b、在非默认的情况下可以使用sizeof去获取自定义的结构体类型的实例它在内存中占的字节数,但是需要把它放在不安全的上下文当中:

  unsafe

   {

    int x=sizeof(Student);

   }

Decimal数据类型精确度比double高占16个字节;

⑩最后一个”基本操作符”:”->”

*类(class)属于引用类型,结构体(struct)属于值类型。C#中有严格的规定像指针操作,取地址操作,用指针去访问成员的操作,只能用来操作结构体类型,不能用它们去操作引用体类型。(Class)

要运行不安全代码除了要把它放在unsafe{}里面,还要再项目->最后一项(相应项目属性)->生成->勾选”允许生成不安全代码”,所谓双重保险。

使用该操作符时要在unsafe情况下使用:

unsafe

            {

                Student stu;

                stu.ID = 1;

                stu.Score = 99;

                Student*pStu=&stu;

                pStu->Score = 100;

                Console.WriteLine(stu.Score);

            }

 

4.2一元操作符:

只要有一个操作数跟在它后面就可以构成表达式,也叫单目操作符。

 

①&x和*x操作符(很少见有印象即可):

这两个操作符同样也需要在不安全的上下文中运行:&是取地址操作符。

简单错误:*pStu.错误:由于.为基本操作符优先级大于*所以是先进行pStu.的操作正确应该为:(*pStu).即加个括号。

 

②+、-、!、~四个一元操作符

-运算符可造成内存溢出:

int a = int.MinValue;

 int b =checked( -a);//原因在于int.MaxValue与int.MinValue绝对值不一样。

 

~求反操作符,对操作数转化为2进制,进行二进制的按位取反。

计算机取相反数的原理:先把该数转化为2进制再按位取反+1 

!操作符只能用来操作布尔类型的数据,

 

③++x;--x运算符:单独使用时x++与++x没有区别。

 

4.3、强制类型转化操作符:(T)x,T表示某种数据类型。

类型转换:

①隐式(implicit)类型转换,常见的为以下三种情况:

*不丢失精度的转换:小字节数(存储空间)的数据类型向多字节(存储空间)的数据类型转换不会丢失精度,比如金鱼从鱼缸里放到游泳池里还是好好的。例如:

  int x = int.MaxValue;

  long y = x;

这就是在不丢失精度的情况下进行的隐式类型转换,具体哪些数据类型可向哪些无丢失精度类型转换见C#语言定义文档6.1.2:

隐式数值转换为:(原则为从小的向大的转)

l 从 sbyte 到 short、int、long、float、double 或 decimal。

l 从 byte 到 short、ushort、int、uint、long、ulong、float、double 或 decimal。

l 从 short 到 int、long、float、double 或 decimal。

l 从 ushort 到 int、uint、long、ulong、float、double 或 decimal。

l 从 int 到 long、float、double 或 decimal。

l 从 uint 到 long、ulong、float、double 或 decimal。

l 从 long 到 float、double 或 decimal。

l 从 ulong 到 float、double 或 decimal。

l 从 char 到 ushort、int、uint、long、ulong、float、double 或 decimal。

l 从 float 到 double。

从 int、uint、long 或 ulong 到 float 的转换以及从 long 或 ulong 到 double 的转换可能导致精度损失,但决不会影响数值大小。其他的隐式数值转换决不会丢失任何信息。

不存在向 char 类型的隐式转换,因此其他整型的值不会自动转换为 char 类型。

 

*子类向父类的转换;

例如定义三个互相继承的类:

复制代码

复制代码

class Animal{public void Eat(){Console.WriteLine("Eat");}}class Human:Animal{public void Think(){Console.WriteLine("think");}}class Teacher:Human{public void Teach(){Console.WriteLine("Teach");}

复制代码

 

在main函数中:

 Teacher t = new Teacher();

            Human h = t;//这种就是子类向父类隐式类型换

而h.只能访问Human类的成员,不能访问Teacher类的成员,因为引用变量只能访问它这个变量的类型它所具有的成员,注意是这个变量的类型是(Human)而不是这个变量引用的类型(Teacher)。(简单的比喻比如人的父类是猴子,但猴子的只能做猴子的行为而不能做人类的行为。)

*装箱;见上一节。

 

②显式(explicit)类型转换:为什么要有?因为这种转换有精度的丢失,编译器要你明明白白的写出来,让你知道有精度的丢失,也要转换数据类型。

*有可能丢失精度(甚至发生错误)的转换,即cast(铸造):在数据前面加个圆括号:(T)x,T里面为强转后的数据类型。

例如:

uint x = 65536;

ushort y = (ushort)x;//这样才能成立,因为ushoort的最大值比x小

具体见语言定义文档6.2.1;注意强制类型转换时由有符号类型向无符号类型转换,符号位拿来当数据了肯定会丢失精度。所以在对有符号类型进行强转时要格外注意。

*拆箱

*使用Convert类:

首先:如下情况:
 string str1=Console.ReadLine();//ReadLine方法是读取一行输入

 string str2 = Console.ReadLine();

 Console.WriteLine( str1+str2);//

若输入:12 12则输出的是1212,原因为由于操作符与数据类型相关联,当+识别到左右两边的数据都是string类型时则把两个字符串连接起来,得到的就是1212的字符串而不是我们想要的值24。

正确做法为: int x = Convert.ToInt32(str1);

            int y = Convert.ToInt32(str2);

这个例子就是非常常见的数据类型转换例子。

 

有的数据类型不能使用cast这种方式如int和string这两种数据类型差距太大。这时需要借助工具类(如Convert类)来进行类型转换。如: Convert.ToInt32(x);这个类相当于一个类型转换枢纽,几乎可以把当前数据类型转换成全部想要的数据类型。

*ToString方法与各类数据类型的Parse/TryParse方法:

有时候我们需要把其他数据如int转化为字符串类型数据这时候有两种方法可以选择:

第一种:调用Conver类的ToString静态方法: Convert.ToString(36种重载形式);详见”强制类型转换ToString”

第二种:调用数值类型数据的ToString实例(非静态)方法来完成转换: x.ToString(3个重载);ToString方法但就是把实例转化为一个字符串,创建一个Object对象:object o;o.会出现四个方法其中就有”ToString”说明所有的数据类型都有这个方法(一切数据类型都由object派生)

Parse(解析):这里的th1.Text和th2.Text都是字符串类型(string)

  double x = double.Parse(th1.Text);

            double y = double.Parse(th2.Text);

可以这样改写,小小的缺点:只能解析格式正确的数据如()里输入1ab格式不对就不能自动解析并转换为double类型。

这时可以使用TryParse方法: double x = double.TryParse();

具体涉及内容以后会讲。

 

③自定义数据类型转换操作符

示例;*操作符的本质就是方法的简记法

例如:创建了两个类:

class Stone

    {

        public int Age;

    }

    class Monkey

    {

        public int Age;

}

在main函数中进行类型转换:

Stone stone = new Stone();

Monkey m = (Monkey)stone;

显然不行编译器不知道给怎么转换,我们要告诉它,由于是stone进行类型转换所以在stone类里面添加具体语句:

class Stone

    {

        public int Age;

        public static explicit operator Monkey(Stone stone)//可以理解为显式类型转换操作符就是一个目标类型的实例的构造器,但是这个构造器不是写在目标类型的类里面而是写在被转换的数据类型里面(而前面有四个操作符public static explicit operator ).

        {

            Monkey m = new Monkey();

            m.Age = stone.Age / 500;//表示如何转换

            return m;

        }

    }

这样编译器就知道要怎么转换了,继续回到main函数:

 Stone stone = new Stone();

 stone.Age = 5000;

 Monkey m = (Monkey)stone;

 Console.WriteLine(m.Age);

按照stone类里面定义(m.Age = stone.Age / 500;)的对stone对象进行转换,输出为:10;

 

该方式为显式转换,隐式只需要: public static implicit operator Money(Stone stone)

即把 explicit改成implicit然后类型转换时: Money m = stone;可以把stone对象的前缀(Money)省略。

 

4.4、算术运算符:详见语言定义文档7.8.1

务必留意”类型提升”:算术运算符都会产生,默认的类型提升如int型的数据与double的数据相乘为了不丢失精度,int型先隐式转换为double再进行计算。

“/“整数除法要注意除0异常,浮点数的除法没有该异常。

 double a = double.PositiveInfinity;//正无穷大

 double b = double.NegativeInfinity;//负无穷大

 

double x=(double)5/4;

结果为浮点型的1.25因为()类型转换操作符优先级大于算术运算符,所以先对5进行类型转换为double再进行除法,除法过程中运算符发现两边类型不同进行”类型提升”

 

有时候取余操作符%也会进行”类型提升”,也有浮点数取余。

 

4.5、位移操作符:<<(左移)、>>(右移)

表示:数据在内存当中的2进制的结构向左或者向右进行一定位数的平移:

int x = 7;

int y = x << 1;//x左移一位

  string strx = Convert.ToString(x, 2).PadLeft(32, '0');

  string stry = Convert.ToString(y, 2).PadLeft(32, '0');

  Console.WriteLine(strx);

  Console.WriteLine(stry);

输出显示为:00000000000000000000000000000111

00000000000000000000000000001110明显左移了一位

当没有溢出的情况下左移就是×2右移就是÷2.

移位过多会产生溢出由于默认是unchecked所以不会有异常。

*左移无论是正负数补进来的都是0,右移如果操作的是正数最高位补进来的是0,负数则补1;

 

4.6、关系操作符<,>,<=,>=;

所有关系操作符的运算结果都是布尔类型的,除了可以比较数值类型还可以比较字符类型。

 char char1='a';

 char char2='A';

 ushort u1 = (ushort)char1;

 ushort u2 = (ushort)char2;结果为u1=97;u2=65,;(对应Unicode码表)

还可以比较字符串,只能比是否相等。

string str1 = "ABc";

string str2 = "Abc";

Console.WriteLine(str1.ToLower()==str2.ToLower());都转换为小写输出结果为相等。

还可以调用: string.Compare();返回正值则第一个大于第二个,负值则小于,0则相等,比较方式为把两个字符串对齐依次比较字母对应的Unicode码。

 

4.7、类型检验操作符,is,as

is操作符结果是布尔类型,作用为检验某个对象是否为某个类(可以判断分类):

Teacher t=new Teacher();

Var result=t is Teacher;

Var result=t is Animal;//由于Teacher的父类为Animal所以这个判断也为true;反过来不行

as操作符的作用为:

Object o=new Teacher();

Teacher t=o as Teacher;即如果o对象是Teacher类则把o对象的地址交给Teacher类的引用变量t,否则把null值给t。

 

4.8、逻辑位与(&),逻辑位或(|),逻辑按位异或(^);

所谓位与就是按位求与,求与时把1当做真,0当做假,真&真为真,真&假为假,假&假为假。例如:

  int x = 7;

            int y = 28;

            int z = x & y;

            string strx = Convert.ToString(x, 2).PadLeft(32, '0');

            string stry = Convert.ToString(y, 2).PadLeft(32, '0');

            string strz = Convert.ToString(z, 2).PadLeft(32, '0');

            Console.WriteLine(strx);

            Console.WriteLine(stry);

            Console.WriteLine(strz);

输出结果为:

00000000000000000000000000000111

00000000000000000000000000011100

00000000000000000000000000000100

按位或(|),真|真为真,假|真为真,假|假为假。

按位异或(^),两位相同为真,不同为假。

 

4.9、条件与(&&),条件或(||)用来操作布尔类型的值,最后结果也是布尔类型的值。
条件与(&&):左右两边都是真,整体值才为真;条件或(||):左右两边有一个真整体即为真。

条件与(&)与条件或(|)具有短路效应: if (x>y&&a++>3)当条件较多时条件与只要发现左边的值为假则不用判断右边的值了直接返回false,条件或情况类似,如果左边为真则直接返回true。

 *由于存在该效应我们写代码时要尽量避免。

 

4.10、null合并操作符(??):

 int ? y=null;//相当于暂时给y赋null值以后可变

 int c = y ?? 1;//判断y是否为null是就用1赋值给y。

 

4.11、条件操作符:?:(是唯一一个可以接收三个数的操作符):就是if..else分支的简写:

 y = (x >= 60) ? 100 : 0;表示x如果>=60就把100赋值给y否则把0赋值给y,往往用圆括号把条件括起来提高可读性。

 

4.12、(优先级最低)赋值表达式与lambda表达式:=,+=,%=,-=,>>=,<<=,&=,^=,|=

表示先运算再赋值,lambda表达式(=>)后面会讲到。

不过都要注意数据类型,运算符会根据两边的数据类型来判断进行的是整数运算还是浮点数运算还是其他。*赋值运算符的运算顺序是从右到左。例如:

Int x=3;

Int y=4;

Int z=5;

Int a=x+=y+=Z;先算右边的y+=z即y=y+z;...

多抽出1分钟来学习,让你的生命更加精彩!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438303.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 有什么惊艳到你的地方?

作者&#xff1a;皮皮关 链接&#xff1a;https://www.zhihu.com/question/335137780/answer/786853293 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 很多游戏开发者都是由于Unity而“被迫”使用C#的。但用过一段…

SqlServer学习之存储过程

前言&#xff1a;对于存储过程一直有一种抵触的心理&#xff0c;因为毕业至今所在的公司开发组都不是很规范&#xff0c;对于开发的一些注意事项并没有很多的规定&#xff0c;只是在知乎上查找相关知识的时候&#xff0c;看到很多人对于在程序里使用存储过程的不好之处都有很多…

中间件之RPC

一、RPC的定义 1、RPC(Romote Procedure Call)&#xff1a;远程过程调用&#xff0c;允许一台计算机程序远程调用另外一台计算机的子程序&#xff0c;不用关心底层网络通信 2、应用&#xff1a;分布式网络通信 3、在Socket的基础上实现&#xff0c;比socket需要更多资源 4、…

需求分析之UML用例图学习

用例图常用的三种关系浅析&#xff1a; &#xff08;一&#xff09;泛化(Inheritance) 通常理解的继承关系 &#xff08;二&#xff09;包含&#xff08;include&#xff09; 分解功能&#xff0c;一定包含的功能 &#xff08;三&#xff09;拓展(extend) 附加功能&#xff0c…

UML之用例图(use case)箭头方向

1、Association&#xff0c;无箭头&#xff0c;Actor连接UseCase即可&#xff1b; 2、DirectedAssocition&#xff0c;Actor连接UseCase&#xff0c;箭头由Actor指向UseCase&#xff08;角色指向用例&#xff09;&#xff1b; 3、Generalization&#xff0c;继承&#xff0c;…

UML-记忆技巧

箭头方向 UML箭头方向&#xff1a;从子类指向父类&#xff0c;读作继承自定义子类时需要通过extend关键字指定父类子类一定时知道父类定义的&#xff0c;但父类并不知道子类的定义 只有知道对方信息时才能指向对方 image.png 实现-继承|虚线-实现 空心三角箭头&#xff1…

国密算法SM2,SM3,SM4-java实现

SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法&#xff0c;基于ECC。其签名速度与秘钥生成速度都快于RSA&#xff0c;非对称加密&#xff0c;该算法已公开 SM3是中华人民共和国政府采用的一种密码散列函数标准&#xff0c;由国家密码管理局于2010年12月17日…

数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释)

一、数据库事务隔离级别 数据库事务的隔离级别有4个&#xff0c;由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable &#xff0c;这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。 √: 可能出现 : 不会出现 脏读不可重复读幻…

原子性和一致性的区别是什么?

注&#xff1a; 重度参考了&#xff1a;https://www.zhihu.com/question/30272728/answer/72476703&#xff08;讲的非常好&#xff0c;逻辑清晰&#xff09; 轻度参考了&#xff1a;https://www.cnblogs.com/fjdingsd/p/5273008.html&#xff08;概念也比较清晰&#xff0c;…

什么是事务、事务特性、事务隔离级别、spring事务传播特性

1.什么是事务&#xff1a; 事务是指程序中的一个操作序列。其特点是&#xff1a;该序列的所有操作要么全部成功完成&#xff0c;要么只要有一个操作失败&#xff0c;则该序列所有操作都将被撤销。这也是事务的原子性&#xff08;要么成功&#xff0c;要么失败&#xff09;。 …

软件工程 – 开发模型

软件工程 – 开发模型 为了指导软件开发&#xff0c;可以用不同的方式将软件生命周期中的所有开发活动组织组织起来从而形成不同的开发模型。 瀑布模式 瀑布模型严格遵守软件生命周期各阶段的固定顺序:计划、分析、设计、编程、测试和维护&#xff0c;上一阶段完成才能进入到…

排序算法之选择排序

动图演示 使用场景:适用于大多数排序,数据量大效率明显优于冒泡 java代码实现 import java.util.Arrays;/*** 选择排序*/ public class SelectSort {private static int[] selectSort(int[] arr) {if (arrnull||arr.length<2){return arr;}//控制循环次数,最后一个元素不用…

VS的包含目录、库目录、引用目录、可执行目录解释

来源&#xff1a;包含目录、库目录、附加包含目录、附加库目录、附加依赖项之详解 学习备份 VS项目中的包含目录、库目录、附加包含目录、附加库目录、附加依赖项均在”项目->属性->配置属性”下进行配置&#xff0c;具体说明如下&#xff1a; VC目录&#xff1a; 包含目…

排序算法之插入排序

动图演示 使用场景:数据有序程度越高,效率越高 java代码实现 import java.util.Arrays;/*** 插入排序*/ public class InsertSort {private static int[] insertSort(int[] arr) {if (arrnull||arr.length<2){return arr;}// 从下标为1的元素开始选择合适的位置插入&…

第一节:复习委托,并且通过委托的异步调用开启一个新线程和异步回调、异步等待。

一. 再谈委托 1. 委托是一个关键字为delegate的自定义类型&#xff0c;通过委托可以把方法以参数的形式传递给另外一个方法&#xff0c;实现插件式的开发模式&#xff1b; 同时调用委托的时候&#xff0c;委托所包含的所有方法都会被实现。 2. 委托的发展历史&#xff1a;new…

第二节:深入剖析Thread的五大方法、数据槽、内存栅栏。

一. Thread及其五大方法 Thread是.Net最早的多线程处理方式&#xff0c;它出现在.Net1.0时代&#xff0c;虽然现在已逐渐被微软所抛弃&#xff0c;微软强烈推荐使用Task(后面章节介绍)&#xff0c;但从多线程完整性的角度上来说&#xff0c;我们有必要了解下N年前多线程的是怎么…

HashMap和ConcurrentHashMap

HashMap: 线程不安全&#xff0c;不支持并发操作&#xff0c;键-值&#xff08;key-value&#xff09;都允许为空、、不保证有序 在 JDK1.7 中&#xff0c;HashMap 采用头插法插入元素&#xff0c;因此并发情况下会导致环形链表&#xff0c;产生死循环。 虽然 JDK1.8 采用了…

第三节:ThreadPool的线程开启、线程等待、线程池的设置、定时功能

一. ThreadPool简介 ThreadPool简介&#xff1a;ThreadPool是一个线程池&#xff0c;当你需要开启n个线程时候&#xff0c;只需把这个指令抛给线程池&#xff0c;它将自动分配线程进行处理&#xff0c;它诞生于.Net 2.0时代。 ThreadPool与Thread的区别&#xff1a; ①&#xf…

第十节:进一步扩展两种安全校验方式

一. 简介 简介&#xff1a; 上一节中&#xff0c;主要介绍了JWT校验&#xff0c;它是无状态的&#xff0c;是基于Token校验的一种升级&#xff0c;它适用的范围很广泛&#xff0c;APP、JS前端、后台等等客户端调用服务器端的校验。本节补充几种后台接口的校验方式&#xff0c;它…

第十一节:WebApi的版本管理的几种方式

一. 背景和方案 1. 多版本管理的概念 Android 、IOS等 App 存在着多版本客户端共存的问题&#xff1a;App 最新版已经升级到了5.0 了&#xff0c;但是有的用户手机上还运行着 4.8、3.9 甚至2.2 版本的 App&#xff0c;由于早期没有内置升级机制、用户不会升级、用户拒绝升级等…