为什么要用泛型?那么先来看下面两个功能相似的非泛型类
这个类都是int
class MyIntStack
{
int[] StackArry;
public void Push(int x)
{
}
}
这个类都是float
class MyFloatStack
{
float[] StackArry;
public void Push(float x)
{
}
}
第一个类实现了int型的功能,第二个类通过剪切、改类名、将int改为float实现float型功能,通过这种方式的变换实现float型功能当然可行,但有以下缺点:
a、需检查类声明哪些需更改,哪些需保留
b、每次添加新类型(long、double...)等时都要重写代码
c、有很多相同代码副本,占用空间
d、调试和维护复杂且易出错
泛型提供了这些问题的更好的解决方式
泛型可让多个类型共享一组代码,泛型允许我们声明类型参数化的代码,可以用不同的类型进行实例化。也就是说,我们可以用“类型占位符”来写代码,然后在创建类的实例时指明真实的类型
C#提供了5种泛型:类、结构、接口、委托和方法
示例:
class Stack<T>
{
T[] StackArry;
public void Push(T x)
{
}
}
注意:
a、在类名之后放置一组尖括号
b、在尖括号中用逗号分隔的占位符字符串来表示希望提供的类型,占位字符串叫做类型参数
c、在泛型类声明的主体中使用类型参数来表示应该替换的类型
class SomeClass<T1, T2>
{
public T1 SomeVar = new T1();
public T2 SomeVar = new T2();
}
作用:告诉编译器能使用哪些真是类型来替代占位符(类型参数),编译器获取这些真实类型来创建构造类型(即创建真实类对象的模板)。要替代类型参数的真是类型叫做类型实参
SomeClass<short,int>
创建了类的模板后,我们还要将他实例化才可使用,实例化也就意味着赋给变量,所以要创建变量
SomeClass<short, int> mySc1 = new SomeClass<short, int>();
var mySc2 = new SomeClass<short, int>();
注意实例化的<>()
using System;
class MyStack<T>
{
T[] StackArray;
int StackPointer = 0;
public void Push(T x)
{
if (!IsStackFull)
StackArray[StackPointer++] = x;
}
public T Pop()
{
return (!IsStackEmpty)
? StackArray[--StackPointer]
: StackArray[0];
}
const int MaxStack = 10;
bool IsStackFull
{
get
{
return StackPointer >= MaxStack;
}
}
bool IsStackEmpty
{
get
{
return StackPointer <= 0;
}
}
public MyStack()
{
StackArray = new T[MaxStack];
}
public void Print()
{
for (int i = StackPointer - 1; i >= 0; i--)
Console.WriteLine("Value:{0}", StackArray[i]);
}
}
class Program
{
static void Main()
{
MyStack<int> StackInt = new MyStack<int>();
var StackString = new MyStack<String>();
StackInt.Push(3);
StackInt.Push(5);
StackInt.Push(7);
StackInt.Push(9);
StackInt.Print();
StackString.Push("This is fun!");
StackString.Push("Hi there!");
StackString.Print();
}
}
知识回顾:
where约束T必须继承自某一接口,方便我们调用T里面的方法。
// IRole的作用是约束传入的两个参数类型必须要实现IRole这个接口;
public bool CompareLevel<T, K>(T t1, K t2) where T : IRole where K : IRole
{
return t1.level >= t2.level;
}
//那么怎么使用泛型呢?
public void Test()
{
//先定义三个测试用的类型
MyNPC npc = new MyNPC();
MyPlayer player = new MyPlayer();
MyMonster monster = new MyMonster();
//对各个类型的level赋值
npc.level = 1;
player.level = 2;
monster.level = 3;
//比较npc和player的level就很简单了,只需要这样调用即可
bool b1 = CompareLevel(npc, player);
bool b2 = CompareLevel<MyNPC, MyMonster>(npc, monster);
}
public interface IRole
{
int level { get; set; }
}
public class MyPlayer : IRole
{
public int level { get; set; }
}
public class MyNPC : IRole
{
public int level { get; set; }
}
public class MyMonster : IRole
{
public int level { get; set; }
}
和非泛型类一样,泛型类的扩展方法:
a、必须声明为static
静态类不必生成新的实例,Main方法可直接调用另一个类(静态类为此类扩展类,间接调用扩展类)
静态类内为静态成员
b、第一个参数类型中必须有关键字this,后面是扩展的泛型类的名字
using System;
class A<T>
{
T[] vals = new T[3];
public A(T T1, T T2, T T3)
{
vals[0] = T1;
vals[1] = T2;
vals[2] = T3;
}
public void GetValues()
{
Console.WriteLine("{0},{1},{2}",vals[0], vals[1], vals[2]);
}
}
static class B
{
static public void Test<T>(this A<T> h)
{
h.GetValues();
}
}
class Program
{
static void Main()
{
var a = new A<int>(5, 6, 7);
a.Test();
}
}
与泛型类相似,泛型结构可以有类型参数和约束。泛型结构的规则和条件与泛型类是一致的
using System;
struct P<T>
where T : struct
{
private T a;
public T b
{
get { return a; }
set { a = value; }
}
}
class Program
{
static void Main()
{
var p = new P<int>();
p.b = 10;
Console.WriteLine(p.b);
}
}
using System;
public delegate string Func<T1, T2, T3>(T1 p1, T2 p2);
class Simple
{
static public string PrintString(int p1,int p2)
{
int total = p1 + p2;
return total.ToString();
}
}
class Program
{
static void Main()
{
//var myDel = new Func<int, int, string>(Simple.PrintString);
//Console.WriteLine("Total:{0}", myDel(15, 13));
Console.WriteLine("Total:{0}", new Func<int, int, string>(Simple.PrintString)(15, 13));
//传入的方法数值应该传入的方法之后!
}
}
本例中委托形参负责将数据传入委托中的方法。委托返回类型为string,是因为委托中的方法返回类型为string,当然委托类型为object也是可以的
泛型接口允许我们编写参数和接口成员返回类型都是泛型类型参数的接口。泛型接口的声明和非泛型接口的声明差不多
using System;
interface Iif<T>
{
T ReturnIt(T invalue);
}
class Simple<T> : Iif<T>
{
public T ReturnIt(T invalue)
{
return invalue;
}
}
class Program
{
static void Main()
{
var a = new Simple<int>();
var b = new Simple<string>();
Console.WriteLine(a.ReturnIt(10));
Console.WriteLine(b.ReturnIt("good"));
}
}
这样可继承多个不同实际类型参数,若是只写继承一个接口而希望实现两个接口,那是系统所不允许的,因为那可能出现两个相同类型参数,造成冲突
using System;
interface Iif<T>
{
T ReturnIt(T invalue);
}
class Simple: Iif<int>,Iif<string>
{
public int ReturnIt(int invaluea)
{
return invaluea;
}
public string ReturnIt(string invalueb)
{
return invalueb;
}
}
class Program
{
static void Main()
{
var a = new Simple();
Console.WriteLine(a.ReturnIt(10));
Console.WriteLine(a.ReturnIt("good"));
}
}
class Simple : Iif<int>, Iif<S> //报错,因为S可能为int型,类会产生两个重复的接口,这是不允许的
{
public int ReturnIt(int invalue)
{
return invalue;
}
public S ReturnIt(S invalue)
{
return invalue;
}
}
泛型接口名字不会和非泛型冲突(若都是泛型或非泛型,出现相同的接口名,则会冲突),我们还可声明一个跟泛型名字一样的非泛型接口,如下代码所示
interface Iif<T>
{
T ReturnIt(T invalue);
}
interface Iif
{
int ReturnIt(int invalue);
}
大家还有什么问题,欢迎在下方留言!