泛型基础
1.为什么要用泛型类,我们先来看一段代码:
以交换两个数为例,可能需要定义多个重载方法
public static void swap(ref int x,ref int y)
{
int temp = x;x=y;y=temp;
}
public static void swap(ref float x,ref float y)
{
float temp = x;x=y;y=temp;
}
public static void swap(ref double x,ref double y)
{
double temp = x;x=y;y=temp;
}
这些方法的实现代码类似,只是操作的数据类型不同而已,这种方法虽然没错,但工作效率低,可维护性差。一但发现错误,就不得不在个个地方重复修改。于是有人就会提出一种新的方法,既然C#中object类是其他任何类的基类,那么把方法中的参数类型改为object类型,不就可以交换任何类型的值了?
public static void swap(ref object x,ref object y)
{
object temp = x;x=y;y=temp;
}
这种方法虽然有可取之处,但又会带来新的麻烦,因为如果对值类型调用此方法,程序需要在值类型与object类之间进行装箱和拆箱的转换,频繁使用会大大降低程序的性能,另外,如果用户试图交换两个不同类型的值(例如一个整形,一个字符串),但这两种类型是不能交换的,程序能通过编译,但运行时会出错。
我们来看下面的代码:
using System;
namespace p9
{
public class abc
{
public static void Main()
{
int a = 10,b = 20;double c = 0.3,d = 1.25;string e = "我爱";string f ="你们";
fanxing<int>.swap(ref a,ref b);
fanxing<double>.swap(ref c,ref d);
Console.WriteLine("{0} {1},{2} {3},{4}{5}",a,b,c,d,e,f);
}
}
public class fanxing<T>
{
public static void swap(ref T x,ref T y)
{
T temp=x;x=y;y=temp;
}
}
}
上面定义的fanxing<T>就是一个泛型类,<>中的T被称为类型参数,类型参数T可以被不同的具体类型所替代(泛型类中每一个T都将被相应类型替代),fanxing<int>等被称为泛型类的构造类型。在Main()方法中,如: fanxing<int>.swap(ref a,ref b); 使用的不是泛型类本身,而是泛型类的构造类型fanxing<int>,泛型类的构造类型和其他类一样可以通过类型名来调用其静态成员,或使用new来创建对象,并通过对象来调用其实例成员。
2.泛型的静态成员
就如其他普通类中的静态成员(字段和方法)只能被类名(不是类的实例)调用一样,泛型类中的静态成员也只能被静态类的构造类名(不是泛型类名)调用。如果上述代码中出现以下代码就会出错:fanxing<int> a = new fanxing<int>(); a.swap(ref a,ref b); fanxing<T>.swap(ref a,ref b);
对于普通类,一个静态字段在内存中最多只有一个备份,对于泛型类,为其创建了多少种(不是多少个)构造类型,一个静态字段就在内存中有多少份备份。和普通类的静态构造函数在类创建实例时先于其他构造函数执行且只执行一次一样,泛型类的静态构造函数也先于其他构造函数执行,不同的是对于不同类型的构造类型(每种构造类型)都要调用一次静态构造函数,而对于其他构造函数,每一种构造类型有几个构造类型就调用几次构造函数。不同类型的构造类型之间不互相影响。如下代码:
using System
namespace p
{
public class abc
{
public static void Main()
{
Contact<int> c1 = new Contact<int>("赵丽");
Contact<int> c2 = new Contact<int>("汤姆");
Contact<double> c3 = new Contact<double>("李明"); }
}
}
public class Contact<T>
{
private string m_name;
private static int m_objects = 0;
private static int m_classes = 0;
public Contact (string name)
{
Console.WriteLine("构造对象:"+name);
m_name = name;
m_objects++;
Console.WriteLine("Contact<{0}>对象数量:{1}",typeof(T),m_objects);
}
static Contact()
{
Console.WriteLine("构造类Contact<{0}>",typeof(T));
m_classes++;
Console.WriteLine("Contact<{0}>类数量:{1}",typeof(T),m_classes);
}
}
输出结果为:
构造类Contact<System.Int32>
Contact<System.Int32>类数量:1
构造对象:赵丽
Contact<System.Int32>对象数量:1
构造对象:汤姆
Contact<System.Int32>对象数量:2
构造类Contact<System.Double>
Contact<System.Double>类数量:1
构造对象:李明
Contact<System.Double>对象数量:1
操作符重载
重载操作符的意思就是,重新定义该操作符的运算方法,类似于声明一个方法,也有形式参数。重载操作符要用到关键字operator。C#中重载操作符的语法是: public static 返回类型 operator 操作符 (形式参数)。注意:重载运算符时,必须用到public和static关键字,并且形式参数不能为空。
重载一元操作符
可重载的一元操作符有: + - ! ~ ++ -- true false (其中true与false的重载必须同时出现,且返回值类型是bool)
using System;
class BooleanInt
{
private int i;
public int Int
{
get {return i;}
set {i = value;}
}
public BooleanInt()
{}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static BooleanInt operator +(BooleanInt bi)
{return new BooleanInt(bi.Int);}
public static BooleanInt operator -(BooleanInt bi)
{return new BooleanInt(-bi.Int);} // @@@@@
public static bool operator !(BooleanInt bi)
{if (bi.Int ==0)
return true;
return false;}
public static BooleanInt operator~(BooleanInt bi)
{return new BooleanInt( ~bi.Int);}
public static BooleanInt operator ++(BooleanInt bi)
{return new BooleanInt(++bi.Int);}
public static BooleanInt operator --(BooleanInt bi)
{return new BooleanInt(--bi.Int);}
public static bool operator true(BooleanInt bi)
{
if (bi.Int==0)
return false;
return true;
}
public static bool operator false(BooleanInt bi)
{
if (bi.Int==0)
return true;
return false;
}
}
class Test
{
public static void Main()
{
BooleanInt bi = new BooleanInt(10);
Console.WriteLine("bi:{0}",bi.Int);
bi=+bi;
Console.WriteLine("+bi:{0}",bi.Int);
bi=-bi;
Console.WriteLine("-bi:{0}",bi.Int);
Console.WriteLine("!bi:{0}",!bi);
Console.WriteLine("!!bi:{0}",!!bi);
bi=~bi;
Console.WriteLine("~bi:{0}",bi.Int);
BooleanInt newBi = ++bi;
Console.WriteLine("newBi = ++bi:{0},{1}",newBi.Int,bi.Int);
newBi = --bi;
Console.WriteLine("newBi = --bi:{0},{1}",newBi.Int,bi.Int);
bi = new BooleanInt(1);
if (bi) //调用true重载
Console.WriteLine("true - BooleanInt(1):true");
else
Console.WriteLine("true - BooleanInt(1):false");
bi =new BooleanInt(0);
if (bi) //也是调用true重载,只是true操作符的重载必须也同样有false操作符的重载
Console.WriteLine("false - BooleanInt(0):true");
else
Console.WriteLine("false - BooleanInt(0):false");
Console.Read();
}
}
输出结果:
bi:10
+bi:10
-bi:-10
!bi:False
!!bi:True
~bi:9
newBi = ++bi:10,10
newBi = --bi:9,9
true - BooleanInt(1):true
false - BooleanInt(0):false
@@@@@处,return new BooleanInt(-bi.Int); 等同于 BooleanInt a =new BooleanInt(-bi.Int);return a; 就是说先创造一个类的实例,同时调用该类的构造函数,再返回该类的对象,返回值取决于BooleanInt类的ToString()方法的字符串返回值。 例如一下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(abc.xie());
Console.ReadLine();
}
}
public class abc
{
public override string ToString() // ToString()是object类的一个方法,每个类都继承了该方法,如果子类不重写该方法,该方法返回的是当前类名的字符串形式。
{
Console.Write("ToString()方法用于将数值转换成字符串,本例为:");
return 123.ToString();
}
public abc(int a)
{
Console.WriteLine("return a,返回对象取决于该对象的ToString()方法");
}
public static abc xie()
{
abc a = new abc(1);
return a;
}
}
}
输出结果为:
return a,返回对象取决于该对象的ToString()方法
ToString()方法用于将数值转换成字符串,本例为:123
如果把return 123.ToString();改为return base.ToString(); 那么输出结果为:
return a,返回对象取决于该对象的ToString()方法
ToString()方法用于将数值转换成字符串,本例为: ConsoleApplication1.abc
也就是说ToString()方法前面有两种参数,一是简单值类型(这时是将简单值类型转换为字符串形式),一是base(这时base指的是基类,在此就是object类,而object类定义的 ToString()方法返回的是当前类名的字符串形式)
重载二元操作符
可重载的二元操作符有: + - * / % & | ^ << >> == != > < >= <= (其中==与!= >与< >=与<=的重载必须同时出现,如果重载了==与!=,最好同时重载Equals(),而这又需要重载GetHashCode()。如果重载了 + - * / % & | ^ << >>,那么也就重载了对应的复合值操作符 += -= *= /= %= &= |= ^= <<= >>=)
例如一下代码:
using System;
class BooleanInt
{
private int i;
public int Int
{
get{return i;}
set{i = value;}
}
public BooleanInt(){}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static BooleanInt operator +(BooleanInt bi1,BooleanInt bi2)
{return new BooleanInt(bi1.Int + bi2.Int);}
public static BooleanInt operator -(BooleanInt bi1,BooleanInt bi2) //@
{return new BooleanInt(bi1.Int + bi2.Int);} /@@
public static bool operator ==(BooleanInt bi1,BooleanInt bi2)
{return bi1.Int==bi2.Int; }
public static bool operator !=(BooleanInt bi1, BooleanInt bi2)
{ return bi1.Int != bi2.Int; }
public override bool Equals(object obj)
{
return base.Equals(obj); //虽然是重载了,但等于没重载,因为内部仍然用的是基类(这里是object类)的Equals()方法,不过这样不会出现警告。
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
class test
{
public static void Main()
{
BooleanInt bi1 = new BooleanInt(8);
BooleanInt bi2 = new BooleanInt(2);
Console.WriteLine("bi1:{0} bi2:{1}",bi1.Int,bi2.Int);
BooleanInt bi = bi1 + bi2;
Console.WriteLine("bi1 + bi2 ={0}",bi.Int);
bi -= bi1; //由于上面@处重载了-,所以-=也被相应的重载了。
Console.WriteLine("bi -= bi1:{0}",bi.Int); //验证了,重载是重新定义操作符的算法,@@处虽然重载了-,但重载的算法是加法。
Console.WriteLine("bi!=bi1:{0}",bi!=bi1);
Console.Read();
}
}
重载转换运算符
语法: public static (implicit|explicit) operator 返回类型(当前类名 变量){具体转换}
implicit声明转换符为隐式 explicit声明转换符为显式
using System;
class BooleanInt
{
private int i;
public int Int
{
get{return i;}
set{i = value;}
}
public BooleanInt(){}
public BooleanInt(int i)
{Int = i;}
public BooleanInt(BooleanInt bi)
{Int = bi.Int;}
public static explicit operator int(BooleanInt bi) //重载(int) 重载后(int)就能把BooleaInt类型的值显式的转换成int类型了。
{return (int)bi.Int;}
}
class test
{
public static void Main()
{
BooleanInt bi = new BooleanInt(8);
int a= (int)bi;
Console.WriteLine(a);
Console.Read();
}
}