第 10、11、12 节 操作符详解

操作符概念

  • 操作符(Operator)也译为“运算符”
  • 操作符是用来操作数据的,被操作符操作的数据称为操作数(Operand)
    :数值类型提升

操作符的本质

  • 操作符的本质是函数(即算法)的“简记法”
    1)假如没有发明“+”,只有Add函数,算式3+4+5将可以写成Add(Add(3,4),5)
    2)假如没有发明“×”,只有Mul函数,算式3+4×5将只能写成Add(3,Mul(4,5)),注意优先级
  • 操作符不能脱离与它关联的数据类型
    1)可以说操作符就是与固定数据类型相关联的一套基本算法的简记法
int a = 5;
int b = 3;
int c = a / b;
Console.WriteLine(c); //输出结果为1
double a = 5.0;
double b = 3.0;
double c = a / b;
Console.WriteLine(c); //输出结果为1.75

2)示例:为自定义数据类型创建操作符


优先级与运算顺序

  • 操作符的优先级
    1)可以使用圆括号提高被括起来表达式的优先级
    2)只有圆括号可以嵌套
    3)不像数学里有方括号和花括号,在C#语言中“[]”和“{}”有专门的用途
  • 同优先级操作符的运算顺序
    1)除了带有赋值功能的操作符,同优先级操作符都是由左向右进行运算
    2)带有赋值功能的操作符的运算符的运算顺序是由右向左
    3)与数学运算不同,计算机语言的同优级级运算没有“结合律”
    3+4+5只能理解为Add(Add(3,4),5)不能理解为Add(3,Add(4,5))

各类操作符的示例

  • 基本操作符
    基本运算符是组成基本表达式的运算符,即不能再被拆分的表达式。

1)成员访问操作符:x . y
功能:访问外层名称空间中的子集名称空间;访问名称空间中的类型;访问类型的静态成员;访问对象的成员。
举例:

System.IO  //访问外层名称空间中的子集名称空间
System.IO.File  //访问IO名称空间中的File类型
System.IO.File.Create("D:\\HelloWorld.txt");  
//访问File类型的静态成员Create方法
Form myForm = new Form();
myForm.Text = "Hello, World!"  //访问myForm对象的Text属性
myForm.ShowDialog();  //访问myForm对象的ShowDialog方法

2)方法调用操作符:f ( x )
f:function的缩写,方法的名称;x:调用的参数。
:一般,在方法名的后面加一对 () 来实现方法的调用。但是,在委托类型里面不需要加 () ,它是让间接地完成方法的调用。例子如下,

Calculator c = new Calculator();
Action myAction = new Action(c.PrintHello); //委托myAction来管理PrintHello方法
myAction(); //调用被它管理方法

3)元素访问操作符:a [ x ]
功能:访问集合中的元素。
举例:
(1)访问数组中的元素,[ 整数 ]
初始化数组

int[] myintArray = new int[10];  //数组长度为10
int[] myintArray = new int[] {1, 2, 3, 4};  //通过花括号直接给出数组的元素
int[] myintArray = new int[10] {1, 2, 3, 4}; 不能这样写,系统不会默认添0

访问数组中的元素
myArray[3] //访问最后一个元素等价于myArray.Length-1
:花括号{}被称为初始化器
(2)访问字典中的元素,[ 不一定是整数 ]
类名< > :泛型类,它本身不是一个完整的类,需要和其他类型组合在一起形成完整的类。例如,Dictionary<string, Student>,其中string作为索引的类型,Student作为值的类型。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OperatorsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, Student> stuDic = new Dictionary<string, Student>(); //泛型类:类名<>,其中string为索引的类型,Student是值的类型
            for (int i = 0; i <= 100; i++)
            {
                Student stu = new Student();
                stu.Name = "s_" + i.ToString();
                stu.Score = 100 + i;
                stuDic.Add(stu.Name, stu); //将索引和值都加载到字典中
            }
            Student number6 = stuDic["s_6"]; //[]中的内容不一定是整数
            Console.WriteLine(number6.Score);                      
        }
    }

    class Student
    {
        public string Name;
        public int Score;
    }
}

4)后置的自增和自减:x ++,x
x++等价于x = x + 1x--等价于x = x - 1
举例:

int x = 100;
int y = x++; 等价于 int y = x; x = x + 1

5)typeof操作符
功能:查看类型的内部结构(Metadata)

Type t = typeof(int);
Console.WriteLine(t.Namespace);
Console.WriteLine(t.FullName);
Console.WriteLine(t.Name);
int c = t.GetMethods().Length;
foreach (var mi in t.GetMethods())
{
   Console.WriteLine(mi.Name);
}
Console.WriteLine(c);

6)default 操作符
(1)default 的操作对象是值类型
结构体类型

int x = default(int); //结果:x = 0
double y = default(double); //结果:y = 0

枚举类型(注意

enum Level
//枚举类型在声明时编译器会将其与整数对应起来
//第一个对应0,依次递增加1
//因此,Level level = default(Level)默认将整数0对应的内容返回
{
    Low, //0
    Mid,  //1
    High  //2
}
enum Level
//倘若枚举类型在声明时采用显示赋值
//则 Level level = default(Level)返回的是整数0对应的内容
//如果任何元素都没有赋值0,则default(Level)返回整数0
{
    Low = 1,
    Mid = 0,
    High = 2
}

(2)default 的操作对象是引用类型

Form myForm = default(Form);
Console.WriteLine(myForm == null); // 结果:ture

7)new
(1)关键字 var :声明隐式类型变量,根据所赋的值来判断变量的类型

var x = 100; //计算机根据100,判断出x的类型是整型,并且后续给x赋值必须为整型
int x = 100; //显示变量,直接给出了变量的类型

(2)new 操作符
需要new操作符
**类类型

new Form(); 
//创建Form类型的实例,通过()调用实例构造器
//获得实例的内存地址
Form myForm = new Form(){Text = "Hello"};
//通过{}调用实例的初始化器
new Form{} {Text = "Hello"}.ShowDialog();

不需要new操作符(C#把new隐藏起来了)“语法糖衣”
**基本类型

int x = 100string name = "Tim"

数组类型

int[] myArray = new int[10];
int myArray = {1,2,3,4}; //使用了“语法糖衣”

**为匿名类型创建对象

var person = new{Name = "Tim", Age = 34};
//new操作符为匿名类型创建对象,用var关键字创建隐式变量来引用该实例
Console.WriteLine(person.Name);
Console.WriteLine(person.Age);
Console.WriteLine(person.GetType().Name); //查看匿名类型的名称

:new操作符功能强大不能乱用,在编写大型程序时,为了避免new操作符造成的紧耦合,可以使用一种依赖注入的设计模式。

(3)new 修饰符

namespace OperatorsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student(); //new操作符
            stu.Report();
            CsStudent CsStu = new CsStudent(); //new操作符
            CsStu.Report();

        }
    }

    class Student
    {
        public void Report()
        {
            Console.WriteLine("I'm a student.");
        }
    }

    class CsStudent : Student
    {
        new public void Report() //new修饰符
        {
            Console.WriteLine("I'm a CS student.");
        }
    }
}

8)checkedunchecked
功能:checked 检查一个值在内存中是否有溢出;unchecked 不用检查是否溢出
**操作符用法

static void Main(string[] args)
{
    uint x = uint.MaxValue;
    Console.WriteLine(x);
    string binStr = Convert.ToString(x, 2);
    Console.WriteLine(binStr);
    try
    {
        uint y = checked(x + 1); //检查是否有溢出,如果有,则跳入catch语句
        Console.WriteLine(y);
    }
    catch (OverflowException ex)
    {
        Console.WriteLine("There's overflow!");
    }
}

**上下文用法

static void Main(string[] args)
{
    uint x = uint.MaxValue;
    Console.WriteLine(x);
    string binStr = Convert.ToString(x, 2);
    Console.WriteLine(binStr);
    checked  //检测checked内部的语句是否有溢出
    {
        try
        {
            uint y = x + 1;
            Console.WriteLine(y);
        }
        catch (OverflowException ex)
        {
            Console.WriteLine("There's overflow!");
        }
    }

9)delegate
功能:声明委托的数据类型。可以用作操作符(已经过时)或者修饰符
**通过 += 操作符将事件挂接到事件处理器上

public MainWindow()
{
    InitializeComponent();
    this.myButton.Click += MyButton_Click; //事件通过+=操作符挂接到事件处理器上
}

private void MyButton_Click(object sender, RoutedEventArgs e)
{
    this.myTextBox.Text = "Hello, World!";
}

**通过delegate操作符声明匿名方法(已经过时)

public MainWindow()
{
    InitializeComponent();
    this.myButton.Click += delegate (object sender, RoutedEventArgs e) 
    //用delegate操作符声明匿名方法已经过时
    {
        this.myTextBox.Text = "Hello, World!";
    };
}

**用 => 代替 delegate 操作符(目前常用的)

public MainWindow()
{
    InitializeComponent();
    this.myButton.Click += (sender, e) =>
    //编译器可以自动推断数据类型
    //“=>”表示将sender和e这两个变量代入到下面的函数体内
    {
        this.myTextBox.Text = "Hello, World!";
    };
}

10)sizeof
功能:获取对象在内存中所占字节数
注意:默认情况下,sizeof 只能用于获取基本数据类型(C#关键字里面除了string和operator的数据类型,因为这两个不是结构体数据类型)的实例在内存中所占的字节数;在非默认情况下,sizeof 可以去获取自定义结构体的实例在内存中占的字节数,但是需要把它放在不安全的上下文中。
**默认情况下

int x =sizeof(double);
int y = sizeof(decimal); 
//decimal(16字节)能够获取比double(8字节)更加精确的数据,一般用在金融方面

**非默认情况下

namespace OperatorsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            unsafe //存放在不安全的上下文中
            {
                int x = sizeof(Student);
                Console.WriteLine(x);
            }
        }
    }

    struct Student
    {
        int ID;
        long Score;
    }
}

11)-> 操作符(只能放在不安全的上下文中使用)

namespace OperatorsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            unsafe
            {
                Student stu;
                stu.ID = 1;
                stu.Score = 99;
                Student* pStu = &stu;
                pStu->Score = 100; //->
                Console.WriteLine(stu.Score);
            }
        }
    }

    struct Student
    {
        public int ID;
        public long Score;
    }
}
  • 一元操作符(单目操作符)
    在操作符的后面跟一个操作数构成表达式

1)& x,* x(两者都需要不安全的上下文)
&:取地址操作符; *:取引用符

unsafe
{
	Student stu;
	stu.ID = 1;
	stu.Score = 99;
	Student* pStu = &stu; //用&获取stu的地址
	pStu->ID = 2; 
	(*pStu).Score = 100; //.的优先级更高,所以需要()来改变运算顺序,*是取内容
}

2)+-~ 操作符
**与数学运算符一致的功能

int x = 100;
int y = -(-x);

**与数学运算符不一致的功能
对正数取相反数时,在数学中y = -x,而计算机语言,二进制的正数求相反数按位取反 + 1
3) 操作符
功能:只能用来操作 bool 类型的值(true、false)

class Program
{
    static void Main(string[] args)
    {
        Student stu = new Student(null);
        Console.WriteLine(stu.Name);
    }
}

class Student
{
    public string Name;
    public Student(string initName)
    {
        if (!string.IsNullOrEmpty(initName))  //!操作符在现实工作中的应用
        {
            this.Name = initName;
        }
        else
        {
            throw new ArgumentException("initName cannot be null or empty.");
        };
    }
}

4)++ x, x
功能:在单独使用时,++x (–x)和 x++(x–) 得到的结果相同;但是,如果有赋值操作符时

int x = 100;
int y = x++; //结果:x=101,y=100 先赋值后自增
int y = ++x; //结果:x=101,y=101 先自增后赋值

5)( T ) x
功能:强制类型转换
(1)隐式(implicit)类型转换
参考《CSharp Language Specification》的 6.1节隐式转换

  • 不可丢失精度的转换
int x = int.MaxValue;
long y = x;
Console.WriteLine(y);
  • 子类向父类的转换

    Human h = t;即完成了由子类向父类的转换。
    虽然,h 引用了 Teacher 的实例,但是在访问该实例时,所用的是 Human 类型的变量 h,而 Human 类型变量里面只有 Eat 和 Think 方法,所以在引用时只有这两个方法。
class Program
{
    static void Main(string[] args)
    {
        Teacher t = new Teacher();
        Human h = t; //子类向父类转换
        Animal a = h; //子类向父类转换
        a.Eat();
    }
}

class Animal
{
    public void Eat()
    {
        Console.WriteLine("Eating……");
    }
}

class Human : Animal
{
    public void Think()
    {
        Console.WriteLine("Who am I ?");
    }
}

class Teacher : Human
{
    public void Teach()
    {
        Console.WriteLine("I teach programming.");
    }
}
  • 装箱
    链接: link.

(2)显式(explicit)类型转换
参考《CSharp Language Specification》的 6.2节显式转换

  • 有可能丢失精度(甚至发生错误)的转换,即cast(铸造)
Console.WriteLine(ushort.MaxValue);
uint x = 65536;
ushort y = (ushort)x;  //强制类型转换
Console.WriteLine(y);  // y = 0

注意:值的大小和正负

  • 拆箱
  • 使用 Convert 类
    方案1:使用 Convert 类的 Tostring 方法
    方案2:直接调用数值数据的实例方法Tostring
private void btn_Click(object sender, RoutedEventArgs e)
{
    double x = System.Convert.ToDouble(tb1.Text);
    double y = System.Convert.ToDouble(tb2.Text);
    double result = x + y;
    this.tb3.Text = System.Convert.ToString(result); //方案1:使用Convert类的Tostring方法
    this.tb3.Text = result.ToString(); //方案2:直接调用数值数据的实例方法Tostring
}
  • ToString方法与各数据类型的Parse/TryParse方法
double x = double.Parse(this.tb1.Text);  //Parse()只能解析格式正确的字符串数据类型
double y = double.Parse(this.tb2.Text);

TryParse() 方法的返回类型是布尔类型(转换成功:true,转换失败:false),该方法通过输出参数将解析好的值返回。

(3)自定义类型转换操作符

  • 示例
    显式类型转换:
class Program
{
    static void Main(string[] args)
    {
        Stone stone = new Stone();
        stone.Age = 5000;
        Monkey wukongSun = (Monkey)stone;  //1. 隐式类型转换:把 (Monkey) 删掉
        Console.WriteLine(wukongSun.Age);

    }
}

class Stone
{
    public int Age;
    
    public static explicit operator Monkey(Stone stone)
    //2. 隐式类型转换:把 explicit 改成 implicit
    {
        Monkey m = new Monkey();
        m.Age = stone.Age / 500;
        return m;
    }
}

class Monkey
{
    public int Age;
}
  • 算数运算操作符(*,/,%,+,-)
    操作符针对不同的数据类型有不同的运算规则。

1)取余操作符:%
浮点类型取余

double x = 3.5;
double y = 3;
Console.WriteLine(x % y);  //取余结果:0.5
  • 位移操作符(<<,>>)
    数据在内存中二进制的结构向左或向右进行一定位数的平移。
    注1:在不产生溢出的情况下,左移即是给数据乘 2,右移即是给数据除 2。
    注2:左移,无论是正数还是负数补进来的均是 0;右移,正数最高位补进来的是 0,负数最高位补进来的是 1。

  • 关系(< > <= >=)和类型检测(is as)
    所有关系运算符得到的结果均是bool类型。

1)is
功能:检验某个对象是否是某个类型的对象,输出结果为 bool 类型

class Program
{
    static void Main(string[] args)
    {
        Teacher t = new Teacher();
        var result1 = t is Human;  // is 操作符
        Car car = new Car();
        var result2 = car is Object;  // 结果:Ture,因为所有类都派生于Object类
        Console.WriteLine(result1.GetType().FullName);
        Console.WriteLine(result1);
    }
}

class Animal
{
    public void Eat()
    {
        Console.WriteLine("Eating……");
    }
}

class Human : Animal
{
    public void Think()
    {
        Console.WriteLine("Who am I ?");
    }
}

class Teacher : Human
{
    public void Teach()
    {
        Console.WriteLine("I teach programming.");
    }
}

class Car
{
    public void Run()
    {
        Console.WriteLine("Running...");
    }
}

2)as

object o = new Teacher();
Teacher t = o as Teacher;  
//判断 o 和 Teacher 是否一样。如果一样,则将对象 o 和它的地址交给 t,否则返回 null
if(t!=null)
{
	t.Teach();
}
  • 逻辑运算符

1)按位求与 x & y

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);

2)按位或: x | y

int x = 7;
int y = 28;
int z = x | y;

3)按位异或:x ^ y

  • 条件运算符
    操作对象是bool类型的值,得到的结果仍是bool类型

1)条件与:&&(并且)
操作符左右两边都是ture时,得到的结果才是ture,否则为false。
2)条件或:|| (或)
只要操作符一边是ture,得到的结果就是ture。

:尽量避开条件与和条件或的短路逻辑!!!

  • null合并操作符(??)

可空数据类型(在数据未写入之前占据一定大小的空位)

Nullable<int> x = null;  //或者 int? x = null
x = 100;
Console.WriteLine(x);
Console.WriteLine(x.HasValue);  //HasValue属性,判断x内是否有值,有ture,无false

其中,Nullable<int>被C#吸收为int?

int? x = null;
int y = x ?? 1;  //如果x是null,则用1来代替
  • 条件操作符(?:)
int x = 80;
string str = (x>=60) ? "Pass" : "Failed";
  • 赋值和lambda表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值