C#中的类型一共分为两大类型:值类型和引用类型。
值类型包括:简单类型,结构类型和枚举类型,其中简单类型又包括:整数类型,实数类型(double,float,decimal),布尔类型(bool),字符类型。这里比较陌生的就是decimal,它表示小数,使用的时候必须在数字后面添加后缀:M(float同理,添加F),否则会被当做double处理。
结构类型就是我们以前的结构体,定义的方式也是一样的:
public struct Student{ public String name; public int number;};
访问结构类型的成员像是这样:Student.name。
结构类型可以有变量(但是不能初始化),也可以有方法,就像类一样,但是不能被继承。使用结构类型的原因就是它是值类型,如果仅仅是一些数据的话,可以放在结构类型中,这样开销会比较小,但如果需要添加方法,就得使用类。枚举类型也是好用的类型,我们可以这样定义一个枚举:
enum City{ SHANGHAI, BEIJING};
因为枚举已经变成了类型(事实上,java的枚举也是一个类),所以我们可以进行类型转换,像是这样:
City city = City.SHANGHAI;int number = (int)city;City city2 = (City)number;
学过java的人都知道,枚举中都是字面常量,我们可以像使用静态字段一样直接使用。C#中的枚举和java的枚举使用方法都是一样,但是,C#可以实现类型转换,这点倒是令我感到惊讶。
引用类型包括:字符串类型,数组类型,类类型,接口类型,委托类型和对象类型。字符串类型和其他语言是一样的,先讲讲字符串的比较,因为我们经常用到。
在java中,习惯使用equals(),不使用==的原因就是它默认是比较引用,但java不能重载运算符。C#中比较的原理是根据字符串中每个字符的编码值(C#使用Unicode编码)。比较的方法很多,但我还是用回我习惯的Equals()好了。
C#中的字符串还有一个非常好用的特性:对于字符串需要进行转义的字符,我们可以直接在该字符串前面添加@字符,就不需要使用反斜杠,像是这样:
String s = @"c:\..";
以前的话,我们就得这样使用:
String s = "c:\\..";
如果需要转义的字符够多,简直就是恶梦!
字符串类型还有一个问题:与其他类型的转化。如果是java,我想把一个字符串转化为相应的int,我会使用Integer.parseInt(),在C#中,我们可以这样使用:
int number = Int32.Parse("123");String str = Convert.ToString(number);
数组类型作为引用类型并不奇怪,java中的数组除了存储基本类型的数组之外,其他都是当做引用数组,但C#不存在基本类型的说法,它们所有的类型都是System.Object,即对象类型。
类类型和接口类型和java差不多,但接口类型不能包含字段,所谓的字段,其实就是初始化的变量,像是int i = 12是不允许的,而且,类的成员变量使用前缀_,而接口类型的变量前缀为I。
重点是委托类型。这是一个神奇的类型,它的作用类似设计模式中常说的委托类。
我们来看看一个简单的委托类型的使用:public delegate double PriceDelegate(); class Price { public double _Price; public void SetPrice(double price) { this._Price = price; } public double GetPrice() { return this._Price; } public double GetDiscountPrice() { return this._Price * 0.9; } }
接着我们来做一个测试:
public static void Main(String[] args){ Price price = new Price(); price.SetPrice(10); PriceDelegate delegatePrice = new PriceDelegate(price.GetPrice); Console.WriteLine(delegatePrice()); delegatePrice = new PriceDelegate(price.GetDiscountPrice); Console.WriteLine(delegatePrice()); }
我们可以看到,策略模式在这里已经被内置了!我们可以利用委托类型简单的实现策略模式(它到底算不算是策略模式还是个问题,因为策略模式是将算法交给具体实现,但显然我们一开始就已经在类中实现了,以后如果想要替换算法,我们就必须到类的实现中修改,当然,使用继承就可以解决这个问题,在派生类中添加新的算法)。
更加严谨的说法应该是充分使用了回调,我们定义好一个方法,然后在需要的时候进行调用。委托类型是有局限的,它规定,作为参数的方法的返回值和参数列表必须和委托类型一样。基于这点,委托类型更接近函数指针,但显然比它好用多了(学习C++的时候,指针的使用一直是我最大的痛苦!)。我们可以想象,在委托类型的运作中,调用作为参数的方法,然后将调用结果返回。委托类型让我想到了javascript,这说明,C#比起java,面向对象的程度更高。说到类型,绕不开的话题就是类型转换,尤其是基本类型转换为引用类型。
java中是利用包装类实现基本类型的包装,但是,C#是利用装箱和拆箱机制。
C#中所有类型都可以向上转型为System.Object,所以,我们将类型转化为Object,这就是装箱(很形象的说法,java是每个基本类型都有一个包装类,就像每种糖果都有自己特有的包装一样,但是,C#直接利用一个Object将所有的东西装起来,不就像一个箱子?),接着再强制转换为我们需要的类型。
我们注意到,其实java的包装机制和C#的装箱机制完全不一样,适用的范围根本不一样,包装机制是将基本类型转化为对应的包装类,装箱机制是将所有值类型转化为Object。
讲到类型转换,C#总是让我大开眼界,像是专门用于转型的is和as操作符,就让我会心一笑。
我们可以使用is操作符来检查一个对象是否兼容于指定的类型,并返回一个Boolean值。
public static void Main(String[] args) { Object obj = new Object(); Boolean boolean = (obj is Object); Console.WriteLine(boolean); }
is操作符永远不会抛出异常,这是值得注意的。我们经常像是这样使用is操作符:
public static void Main(String[] args) { int number = 5; float f = 6.0F; Show(number); Show(f); } public static void Show(Object number) { if (number is int) { Console.WriteLine(number); } else { Console.WriteLine("请重新输入一个整数"); } }
嗯,java的instanceof关键字跑到我脑海中了,如果没有学过java,请无视这句。
as操作符用于简化类型转换:
public static void Main(String[] args) { Object obj = new Object(); People people = obj as People; Show(people); } public static void Show(Object obj) { if (obj is People) { Console.WriteLine(obj); } else { Console.WriteLine("请给我一个人"); } } class People { private String _Name = "人"; public override String ToString() { return _Name; } }
as操作更多是简化我们上面的is操作:
public static void Show(Object obj) { People people = obj as People; if (people != null) { Console.WriteLine(people); } else { Console.WriteLine("请给我一个人"); } }
如果as操作符进行转换时,发现类型不匹配,并不会报错而是返回一个null。
C#中有很多神奇的东西,值得我们继续研究下去。