运算符中一些简单的其实跟C语言差不多,但是还是很有C#特色的。
下列C#所有运算符:
操作符其实有时候不仅仅有单一的意义。操作符是什么意思取决于他操作的变量类型。如下面代码中:
static void Main()
{
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
Console.WriteLine(str1+str2);
}
操作符+在此处意思是连接两个字符串。
操作符其实也可以自己定义,这取决与你所需要操作的变量是什么。下面我们来看看具体如何自定义操作符。
namespace ConsoleAppPractice
{
class Program
{
static void Main()
{
Person papa = new Person();
Person mom = new Person();
papa.Name = "Jack";
mom.Name = "Rose";
List<Person> nation = Person.GetMarry(papa, mom);
foreach(var p in nation)
{
Console.WriteLine(p.Name );
}
}
}
class Person
{
public string Name;
public static List <Person> GetMarry(Person p1,Person p2)
{
//list是一个泛类,只能与尖括号中的其他类构成一个完整的类
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;
}
}
}
以上所隐含的加法运算,意义是将两个人marry形成11个孩子。因而此处我们可以引入类加法定义:
public static List <Person>operator +(Person p1,Person p2)
List<Person> nation = papa + mom;
只需改变这两行代码即可。
由此可以明确,其实操作符本质上还是函数,有特定需要的参数类型和功能。使用操作符是简化了书写。
注:
关于此处operator后面所跟着的那个符号,我试了一下,可能因为这个是二元的吧,似乎只有+、-、/、%、^、*可用。
其中有一个很有意思的点就是,如果要定义一个新的小于<,系统会要求你也要同时定义一个大于>。
如果使用!=和==,系统会报warning: 'Person' defines operator == or operator != but does not override Object.GetHashCode() 看来运算符也有覆盖这一说,可以联想到等会会写到的子类与父类。
再者,这样的操作符重载必须写在要定义的类里,如例子中只能写在Person类不能写在program类不然会报错“One of the parameters of a binary operator must be the containing type”。血淋淋的教训www
一、基本运算符
1、x.y成员访问操作符
也成为点操作符,可以用来访问外层名称空间中的子集名称空间,还可以访问名称空间内的类型,还可以访问类型的静态成员,还可以访问对象成员。总之哪里有成员哪里有他。
2、[]操作符
访问集合中的元素,比如说数组和字典。
在C#中,声明数组的方法是在数据类型后面加[]。如:
int[] a = new int[5] { 1, 2, 3, 4, 5 };
//可在后面加上{}来初始化数组。默认为0.
A[] a = new A[2] { new A(), new A() };
//对象集合需要每个元素都进行一次引用初始化
Form myForm = new Form(){Text = "Hello"};
//{}这个符号是初始化器喔
下面我们来看看访问字典的方式。
namespace ConsoleAppPractice
{
class Program
{
static void Main()
{
Dictionary<string, Student> stuDic = new Dictionary<string, Student>();
for (int i = 0; i <=100; i++)
{
Student stu = new Student();
stu.name = "s_" + i.ToString();
stu.score = 100;
stuDic.Add(stu.name, stu);
}
Student number6 = stuDic["s_6"];
Console.WriteLine(number6 .score );
}
}
class Student
{
public int score;
public string name;
}
}
Dictionary是一个泛类。它尖括号中第一个参数是索引,即你需要通过这个来查找某个元素。第二个参数自然就是字典里面的元素的数据类型。
注:自创建索引:
差不多是属性格式。
public int[] this [int index]
{
get
{
return this.values[index];
}
}
3.typeof
static void Main()
{
Type t = typeof(int);
Console.WriteLine(t.Name );
Console.WriteLine(t.AssemblyQualifiedName );
Console.WriteLine(t.Namespace );
int c = t.GetMethods().Length;
foreach (var mi in t.GetMethods())
{
Console.WriteLine(mi.Name );
}
Console.WriteLine(c);
}
使用typeof来看一个数据类型的相关操作。
namespace ConsoleAppPractice
{
class Program
{
static void Main()
{
Type c = typeof(person);
Console.WriteLine(c.GetMethods ().Length );
Console.WriteLine(c.Name);
Console.WriteLine(c.FullName );
foreach (var mi in c.GetMethods())
{
Console.WriteLine(mi.Name );
}
}
}
class person
{
public double Add(double a,int b)
{
return a + b;
}
public static int Add(int a,int b)
{
return a + b;
}
double Add(int a, double b)
{
return a + b;
}
}
}
//运行结果:
/*
6
person
ConsoleAppPractice.person
Add
Add
Equals
GetHashCode
GetType
ToString
*/
自定义的类,里面不public的方法就不会计算在typeof所包含的方法里。
4、default
功能是获取一个类型的默认值
int x = default(int);
当default发现为结构体时返回0;如果是枚举类型,返回枚举类型里面对应为0的那个东西;如果是类,返回null。
用default获取枚举值时要小心,在设计枚举类型时也要记得搞出一个0。
5、new操作符
首先介绍一下var这个关键字,之前文章就有提到过,它可以帮助我们生成隐式类型的变量。
接着说new。new的用处还蛮多的。作为操作符,new可以在内存中构建一个实例,并且立即调用实例构造器,再将地址穿给引用变量,构造了一个桥梁。
new操作符还能调用{}初始化器:
int[] a = new int[5] { 1, 2, 3, 4, 5 };
A[] a = new A[2] { new A(), new A() };
Form myForm = new Form(){Text = "Hello"};
但注意不是所有的类创建实例都需要new操作符,比如说string就不需要。是微软为了统一基本操作类型的体验才对string进行了简化。这一过程叫作语法糖衣。{很形象!}其实数组也有这个现象发生。你可以直接int[] a = {1,2,3,4,5};
new有一个非常重要的用途,就是对匿名类型的创建。
var person = new { Name = "Mr.Okay", Age = 32 };
如果这时候对person变量进行gettype操作,会得到如下type:
<>表示这个类型是泛类型,‘2表示构成这个类型需要两个其他的类型。
由着我们才可以发现var与new这个组合技的强大之处。平时我们写代码也可以在设对象时偷懒写var,这是被鼓励的一个方式。
但事实上,功能越强大,new操作符就越危险。因为new操作符事实上紧紧耦合了main所在这个类与调用的类,使得类间的依赖关系增强。如果是大程序,我们常用依赖注入的方法将其变成松耦合。
其实new这个关键字还有很多别的用处。比如说,除了作为操作符,它还可以作为函数的修饰符。例如以下代码:
class Program
{
static void Main()
{
//派生类会自动继承其父类里面所有的内容
Student stu = new Student();
stu.haha();
CsStudent csStu=new CsStudent();
csStu.haha();
}
}
class Student
{
public void haha()
{
Console.WriteLine("hello!");
}
}
//类与类之间有种关系叫继承(也叫做派生)
class CsStudent : Student
{
new public void haha()//除了有new外其他都与父类相同
{
Console.WriteLine(" world!");
//new操作符会覆盖掉从父类继承来的函数
//子类对父类的方法进行隐藏
}
}
我们使用了:来声明一个派生的类,这个类继承了其父类(:右边那个类)的所有内容。
但是如果此时在子类里使用new操作符,就会发生子类对父类的隐藏。
6、checked和unchecked
用来检验数值溢出、数组越界等等功能(似乎不能检查栈溢出)
如果检测到错误,将错误打在屏幕上,并终止运行程序。
使用checked有两种方法:
//方法1:
static void Main()
{
int x = int.MaxValue;
checked
{
x++;
}
}
//方法2:
static void Main()
{
int x = int.MaxValue;
int y = checked(x + 1);
}
我们一般与try-catch语句结合:
static void Main()
{
try
{
checked
{
int x = int.MaxValue;
x++;
}
}
catch (OverflowException us)
{
Console.WriteLine("error!");
throw;
}
}
catch语句里面的那个throw的有无还是有点区别的。
7.delegate
其实他作为委托类型更广泛,现在很少用来操作符了。landa表达式把他取代了。
他主要用来声明一个匿名的方法。
8、sizeof
sizeof用以获取基本数据类型中的结构体【就是那些关键字啦】所占的字节数。当然除了string和object。因为他们是类。
如果想获取自定义的结构体的字节数,需要将其放在不安全的上下文。跟C语言一样,此处结构体内存计算也存在内存对齐现象。如结构体中一个int一个long,最后得到结果是16而不是12。
9.->
在C语言中是指针访问成员的操作符。
在C#中必须在不安全的情况下使用,并且C#语言严格规定,取地址操作和指针操作只能用在结构体。
二、一元操作符
1.-
我们得到负数的方式,是写出数字的原码,然后除了符号位,按位取反再加一。
在计算机内部,将一个负数变成他的绝对值,是在补码基础上,包括符号位按位取反再加一。
2.!
可用于安全性的检验。
namespace ConsoleAppPractice
{
class Program
{
static void Main()
{
var a = new Student("zz");
Console.WriteLine(a.name );
}
}
class Student
{
public string name;
public Student(string ininame)
{
if (!string.IsNullOrEmpty(ininame ))
{
this.name = ininame;
}
else
{
throw new ArgumentException("error!");
}
}
}
}
3、(T)x
这是强制类型转换。这货很重要,下期再接着讲。
4.*/%
与操作变量有关,跟C语言差不多,不多作赘述。
注意一下NAN=∞*0=x/0
∞:infinity
还有C#中浮点数之间也是可以取余的。
static void Main()
{
double x = 3.5;
double y = 3;
Console.WriteLine(x%y);
}
//运行结果:0.5
5.>>与<<
在不溢出的情况下,左移就是乘2,右移就是除2.
右移,如果是正数,最高位补0,负数补1.
6.逻辑运算符
C#支持浮点数与整型的比较。
string类型只能比较是否相等。如果要忽略大小写:
先全部转为小写再比较。
static void Main()
{
string str1 = "Abc";
string str2 = "abc";
Console.WriteLine(str1.ToLower ()==str2.ToLower());
}
三、其他
1.is
用于检测一个变量牵着的实例是否是某个类型,返回布尔值
namespace ConsoleAppPractice
{
class Program
{
static void Main()
{
haha ha = new haha();
var c = ha is Program;//注意is的左边操作对象是实例不能是类
Console.WriteLine(c.GetType().Name);
Console.WriteLine(c);
}
}
class haha : Program
{
public static int a = 0;
}
}
//输出结果:Boolean True
static void Main()
{
haha ha = null;
var c = ha is Program;
Console.WriteLine(c);
}
//输出结果:False
其实感觉is也可以用于数比较哪,还可以用于检测是否为空。
static void Main()
{
int x = 2;
Console.WriteLine(x is 2);
}
//运行结果:True
2.as
有点C语言三元操作符?那味了。
下段代码意思是,o牵着的实例是不是haha类型的?是的话,将o的对象的地址交给t,不是的话传NULL值。
static void Main()
{
haha o = new haha();
o.a = 2;
haha t = o as haha;
Console.WriteLine(t.a);
}
//输出结果:2
注意:此处如果将o作为object,是不可以调用haha这个类里面的东西的,因为当你试图用引用变量来访问实例内容的时候,只能访问这个引用变量的类型所具有的成员。
3.?可空操作符
null只能赋值给引用变量,不能赋值给值变量。
但是你如果非要,有两种方法。
一是使用泛类型:
static void Main()
{
Nullable<int> x = null;
}
二就是使用可空操作符?
因为可空类型常用,故而已经被吸收成关键字惹。
static void Main()
{
int? x = null;
Console.WriteLine(x is null);
}
//运行结果:True
这三个类型似乎是int?所特有的。第一个是看有没有值,返回True或False.
第二个是看什么值,如果为空程序崩溃。
第三个是看有没有值和值是什么,有值则返回值,没有返回0.
string类型的空值可以:
string str = string.Empty;
4.null合并操作符??
x是空吗?是的话,0赋值给y,x保持不变。
static void Main()
{
int? x = null;
int y = x ?? 0;
Console.WriteLine(x);
Console.WriteLine(y);
}
//输出结果: (空)0
5.条件操作符?:
下面两段代码是等价的
static void Main()
{
string str = string.Empty;
string x = Console.ReadLine();
int score = Convert.ToInt32(x);
if (score >= 60)
{
str = "Pass";
}
else
{
str = "Failed";
}
Console.WriteLine(str);
}
static void Main()
{
string x = Console.ReadLine();
int score = Convert.ToInt32(x);
string str = (score >= 60) ? "Pass" : "Failed";
Console.WriteLine(str);
}
//score大于60吗?是的话,返回pass;不是的话,返回failed。