当前位置: 移动技术网 > IT编程>开发语言>.net > .NET开发基础:从简单的例子理解泛型

.NET开发基础:从简单的例子理解泛型

2019年04月19日  | 移动技术网IT编程  | 我要评论

蓄电池隔板,盐城讯源,win8.1企业版

前言
 .net开发基础系列文章,对自己之前写过的代码备忘,如能给人予帮助,不甚荣幸。个人能力有限,如有差错或不足,请及时指正。
 
从简单的例子理解泛型
话说有家影视公司选拔偶像派男主角,导演说了,男演员,身高是王道。于是有下面代码:
 
//男演员实体类
public class boy
{
    //姓名
    private string mname;
    //身高
    private int mheight;
    public string name {
        get { return this.mname; }
    }
    public int height {
        get { return this.mheight; }
    }

    public boy(string name, int height) {
        this.mname = name;
        this.mheight = height;
    }
}
 
 
//演员选拔类
public class compare
{
    //导演导超女出生,喜欢一对一pk
    public boy whoisbetter(boy boy1, boy boy2)
    {
        if (boy1.height > boy2.height)
        {
            return boy1;
        }
        else
        {
            return boy2;
        }
    }
}
 
 
//测试
static void main(string[] args)
{
    boy boy1 = new boy("潘长江", 165);
    boy boy2 = new boy("刘德华", 175);

    console.writeline(new compare().whoisbetter(boy1, boy2).name);
    console.readline();
}
 
代码很简单,boy为男演员实体类,包含姓名和身高两个字段属性;compare类中的whoisbetter为选拔逻辑方法,负责选出两个男演员中较高的那个;测试结果:刘德华完胜。
 
任何行业都是一样,需求变更无处不在。第二天,需要选女主角,导演说了,女演员,苗条是王道。于是代码变更,添加了女演员实体类,添加了女演员的选拔方法:
 
//添加女演员实体
public class girl
{
    //姓名
    private string mname;
    //体重
    private int mweight;
    public string name
    {
        get { return this.mname; }
    }
    public int weight
    {
        get { return this.mweight; }
    }

    public girl(string name, int weight){
        this.mname = name;
        this.mweight = weight;
    }
}
 
 
//演员选拔类中添加一个女演员方法
public class compare
{
    //男演员身高是王道
    public boy whoisbetter(boy boy1, boy boy2)
    {
        if (boy1.height > boy2.height)
        {
            return boy1;
        }
        else
        {
            return boy2;
        }
    }

    //女演员苗条是王道
    public girl whoisbetter(girl girl1, girl girl2)
    {
        if (girl1.weight < girl2.weight)
        {
            return girl1;
        }
        else
        {
            return girl2;
        }
    }
}
 
 
//测试
static void main(string[] args)
{
    boy boy1 = new boy("潘长江", 165);
    boy boy2 = new boy("刘德华", 175);

    girl girl1 = new girl("巩俐", 120);
    girl girl2 = new girl("周迅", 80);

    console.writeline(new compare().whoisbetter(boy1, boy2).name);
    console.writeline(new compare().whoisbetter(girl1, girl2).name);
    console.readline();
}
 
结果选出了身高更高的刘德华,选出了体重更轻的周迅,导演很满意。但从程序设计角度,这段代码显然不够完美,第一天选男主角,第二天选女主角,往后还要选男配角,选女配角,选群众......按目前方式,只有往compare类里不断添加方法才能满足导演需求,方法会越来越多,代码会越来越长。于是,我决定修改whoisbetter方法,让它以后可以支持男主,女主,男配,女配,男群众,女群众甚至支持所有两个对象之间的比较:
 
/// <summary>
/// 男演员:实现icomparable接口
/// </summary>
public class boy : icomparable
{
    //姓名
    private string mname;
    //身高
    private int mheight;
    public string name {
        get { return this.mname; }
    }
    public int height {
        get { return this.mheight; }
    }

    public boy(string name, int height) {
        this.mname = name;
        this.mheight = height;
    }

    public int compareto(object obj)
    {
        //比较身高
        return this.mheight - ((boy)obj).height;
    }
}

/// <summary>
/// 女演员:实现icomparable接口
/// </summary>
public class girl : icomparable
{
    //姓名
    private string mname;
    //体重
    private int mweight;
    public string name
    {
        get { return this.mname; }
    }
    public int weight
    {
        get { return this.mweight; }
    }

    public girl(string name, int weight){
        this.mname = name;
        this.mweight = weight;
    }

    public int compareto(object obj)
    {
        //比较体重
        return ((girl)obj).weight - this.mweight;
    }
}
 
首先让实体类支持自定义的比较,男演员比较身高,女演员比较体重。自定义比较是通过实现icomparable接口完成的,在c#里但凡可以比较的类型,比如int、double、char等都实现了icomparable接口。关于icomparable接口此处不作详述,请读者自行查阅相关资料。
 
public class compare
{
    //万物皆object
    public object whoisbetter(object obj1, object obj2)
    {
        object result = obj2;
        //判断比较类型必须相同
        if (obj1.gettype() == obj2.gettype())
        {
            switch (obj1.gettype().tostring())
            {
                //男演员选拔
                case "generic.boy":
                    if (((boy)obj1).compareto(obj2) > 0)
                    {
                        result = obj1;
                    }
                    break;
                //女演员选拔
                case "generic.girl":
                    if (((girl)obj1).compareto(obj2) > 0)
                    {
                        result = obj1;
                    }
                    break;
                //扩展int类型比较
                case "system.int32":
                    if (((system.int32)obj1).compareto(obj2) > 0)
                    {
                        result = obj1;
                    }
                    break;
                }
            }
            return result;
        }
    }
 
修改whoisbetter方法,除了支持对男演员、女演员的比较,为了展示其扩展性,还新增了int类型的比较。
 
//测试
static void main(string[] args)
{
     boy boy1 = new boy("潘长江", 165);
     boy boy2 = new boy("刘德华", 175);

     girl girl1 = new girl("巩俐", 120);
     girl girl2 = new girl("周迅", 80);

     console.writeline(((boy)new compare().whoisbetter(boy1, boy2)).name);
     console.writeline(((girl)new compare().whoisbetter(girl1, girl2)).name);
     console.writeline(new compare().whoisbetter(boy1.height, boy2.height));
     console.writeline(new compare().whoisbetter(girl1.weight, girl2.weight));

     console.readline();
}
 
测试结果:
刘德华
周迅
175
120
ok,截止目前,似乎比较完美了,男演员比身高,女演员比体重,还支持int类型比大小,whoisbetter方法具有了重用性,如果有需要,往后还能扩展,拿来比较任意两个对象。在泛型出现以前,似乎确实比较完美,但这也只是相对的,我们来看看目前代码的弱点:
弱点1:方法的重用性
假设我们要让whoisbetter方法支持更多类型,比如支持基本的double,char,bool类型,支持以后导演可能提出的配角比较,群众比较,那么就必须不断的扩展方法内部代码,这带来极大的维护成本。
弱点2:类型安全问题
 
//测试
static void main(string[] args)
{
    boy boy1 = new boy("潘长江", 165);
    boy boy2 = new boy("刘德华", 175);

    girl girl1 = new girl("巩俐", 120);
    girl girl2 = new girl("周迅", 80);

    console.writeline(((boy)new compare().whoisbetter(boy1, girl1)).name);
    console.readline();
}
 
如上代码我拿潘长江跟巩俐去比较。虽然万能的object给我们带来了便捷,同时也带来了风险,这段代码编译完全可以通过,但运行时会出现异常,girl对象是没法转换为boy类型的,现实里去韩国可以变性,但代码里绝对不行。所以这个方法就像颗定时炸弹,一不小心传错了参数,就会导致严重后果,并且编译阶段完全不被发现。
弱点3:装箱拆箱导致的性能问题
当向whoisbetter方法中传递int参数时,object转换为int导致了拆箱操作:
if (((system.int32)obj1).compareto(obj2) > 0)
反编译获取msil:
il_0093:  unbox.any  [mscorlib]system.int32
c#是强类型语言,但只要引用类型与值类型的相互转换,就避免不了box与unbox。有关装箱与拆箱的知识请读者自行查阅相关资料,此处不作详述。
 
理解泛型
ok,到泛型登场了,摘录了一段msdn中对泛型的描述:泛型类和泛型方法同时具备可重用性、类型安全和效率,这是非泛型类和非泛型方法无法具备的。这三点,跟我们上面的例子相吻合。
看看使用泛型的解决方案:
 
public class compare<t> where t : icomparable
{
    public t whoisbetter(t t1, t t2)
    {
        if (t1.compareto(t2) > 0)
        {
            return t1;
        }
        else
        {
            return t2;
        }
    }
}
 
 
//测试
static void main(string[] args)
{
    boy boy1 = new boy("潘长江", 165);
    boy boy2 = new boy("刘德华", 175);

    girl girl1 = new girl("巩俐", 120);
    girl girl2 = new girl("周迅", 80);

    console.writeline((new compare<boy>().whoisbetter(boy1, boy2)).name);
    console.writeline((new compare<girl>().whoisbetter(girl1, girl2)).name);
    console.writeline(new compare<int>().whoisbetter(boy1.height, boy2.height));
    console.writeline(new compare<string>().whoisbetter(boy1.name, girl1.name));
    console.readline();
}
 
这段代码在优雅度上完胜非泛型,并且可重用性大大提升,可以说它支持所有类型的比较,只要这个类型实现了icomparable接口,同时一劳永逸,不再需要在方法内部作任何扩展。
public class compare<t> where t : icomparable{
    //...
}
泛型类的定义是在类名后面跟上<t>,这个是泛型专用语法,t表示传递进来的类型,你也可以用别的字母代替。
where t : icomparable ,从字面上就能理解,这段表示对t的类型约束。程序是遵循人的意志来执行的,按前面的例子,如果莫名其妙的让程序比较两个object,它没办法知道该去怎么比较。所以我们必须告诉程序,t必须是可比较的类型,t必须实现了icomparable接口。
关于泛型参数约束,msdn提供了一张表格:

  

约束 说明
t:结构 类型参数必须是值类型。可以指定除nullable 以外的任何值类型。
t:类 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
t:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
t:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
t:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
t:u 为t 提供的类型参数必须是为u 提供的参数或派生自为u 提供的参数。
 

visual studio 2010, .net framwork 4.0
 

 

摘自 吴剑-web应用
 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网