C#

C#泛型笔记

这个类和object实现的类有截然不同的区别:

1.       他是类型安全的。实例化了int类型的栈,就不能处理string类型的数据,其他数据类型也一样。

2.       无需装箱和折箱。这个类在实例化时,按照所传入的数据类型生成本地代码,本地代码数据类型已确定,所以无需装箱和折箱。

3.       无需类型转换。


public class Node<T, V> where T : Stack, IComparable

        where V: Stack

    {...}

以上的泛型类的约束表明,T必须是从Stack和IComparable继承,V必须是Stack或从Stack继承,否则将无法通过编译器的类型检查,编译失败。通用类型T没有特指,但因为C#中所有的类都是从object继承来,所以他在类Node的编写中只能调用object类的方法,这给程序的编写造成了困难。比如你的类设计只需要支持两种数据类型int和string,并且在类中需要对T类型的变量比较大小,但这些却无法实现,因为object是没有比较大小的方法的。 了解决这个问题,只需对T进行IComparable约束,这时在类Node里就可以对T的实例执行CompareTo方法了。这个问题可以扩展到其他用户自定义的数据类型。

如果在类Node里需要对T重新进行实例化该怎么办呢?因为类Node中不知道类T到底有哪些构造函数。为了解决这个问题,需要用到new约束:

public class Node<T, V> where T : Stack, new()

        where V: IComparable

需要注意的是,new约束只能是无参数的,所以也要求相应的类Stack必须有一个无参构造函数,否则编译失败。

C#中数据类型有两大类:引用类型和值类型。引用类型如所有的类,值类型一般是语言的最基本类型,如int, long, struct等,在泛型的约束中,我们也可以大范围地限制类型T必须是引用类型或必须是值类型,分别对应的关键字是class和struct:

public class Node<T, V> where T : class

        where V: struct



泛型方法
泛型不仅能作用在类上,也可单独用在类的方法上,他可根据方法参数的类型自动适应各种参数,这样的方法叫泛型方法。看下面的类:

public class Stack2

    {

        public void Push<T>(Stack<T> s, params T[] p)

        {

            foreach (T t in p)

            {

                s.Push(t);

            }

        }

}

原来的类Stack一次只能Push一个数据,这个类Stack2扩展了Stack的功能(当然也可以直接写在Stack中),他可以一次把多个数据压入Stack中。其中Push是一个泛型方法,这个方法的调用示例如下:

Stack<int> x = new Stack<int>(100);

    Stack2 x2 = new Stack2();

    x2.Push(x, 1, 2, 3, 4, 6);

    string s = "";

    for (int i = 0; i < 5; i++)

    {

        s += x.Pop().ToString();

    }    //至此,s的值为64321


在C#1.x中,我们知道类的静态成员变量在不同的类实例间是共享的,并且他是通过类名访问的。C#2.0中由于引进了泛型,导致静态成员变量的机制出现了一些变化:静态成员变量在相同封闭类间共享,不同的封闭类间不共享。



泛型类中的方法重载
方法的重载在.Net Framework中被大量应用,他要求重载具有不同的签名。在泛型类中,由于通用类型T在类编写时并不确定,所以在重载时有些注意事项,这些事项我们通过以下的例子说明:

public class Node<T, V>

    {

        public T add(T a, V b)          //第一个add

        {

            return a;

        }

        public T add(V a, T b)          //第二个add

        {

            return b;

        }

        public int add(int a, int b)    //第三个add

        {

            return a + b;

        }

}

上面的类很明显,如果T和V都传入int的话,三个add方法将具有同样的签名,但这个类仍然能通过编译,是否会引起调用混淆将在这个类实例化和调用add方法时判断。请看下面调用代码:

Node<int, int> node = new Node<int, int>();

    object x = node.add(2, 11);

这个Node的实例化引起了三个add具有同样的签名,但却能调用成功,因为他优先匹配了第三个add。但如果删除了第三个add,上面的调用代码则无法编译通过,提示方法产生的混淆,因为运行时无法在第一个add和第二个add之间选择。

Node<string, int> node = new Node<string, int>();

        object x = node.add(2, "11");

   这两行调用代码可正确编译,因为传入的string和int,使三个add具有不同的签名,当然能找到唯一匹配的add方法。

函数方法的调用首先是匹配最明确的方法,如果不能匹配,再匹配较为模糊的方法


就是返回T的默认值。
比如说T的类型是int类型的,那么这个default(T)的值就是0的
如果是string类型的话,这个返回值就是“”空字符串的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值