浅谈C#泛型
一.为什么要提出泛型的概念
我们在声明对象或者方法中,对象中成员变量的定义或者函数参数都传递都要指定具体的对象类型,但是有的时候参数的类型是变化的,但是实现的功能却又差不多,这个时候我们就想,是否存在一种东西可以将参数的位置“占住”,当传递具体的对象类型是再用这个类型取替换被占住的位置,这个时候就提出了泛型的概念,是不是有点绕,但是看完下面的例子就清除这里表达的内容了,泛型有多种表现形式,泛型类,泛型方法,泛型集合,泛型委托,可以说不懂泛型就没有真正的了解C#,下面让我们来开始泛型的学习吧。
二.泛型类,泛型方法
我们先举个例子,我们定义一个类来模拟入栈出栈操作,我们操作出栈入栈时要针对各种数据类型,int型,double 型,字符型......总之各种类型都有可能,我们不可能针对每个类型都写一个类来操作出栈入栈,这显然是不现实的,这个是时候就该泛型大显身手发时候了,看下面的定义:
public class MyStack<T> //多种类型时可以用T1,T2,T3......来表示
{ public T[] objStack;
public int _stackPoint;
public int _stackSize;
public MyStack(int Size) //成员变量一般在初始化的时候都要赋值
{
objStack = new T[Size];
_stackSize = Size;
_stackPoint = -;
} /// <summary>
/// 入栈操作
/// </summary>
/// <param name="item"></param>
public void Push(T item) //这里要把T当成一种数据类型
{
if (_stackPoint > _stackSize)
{
return;
}
else
{
_stackPoint++;
objStack[_stackPoint] = item;
}
} /// <summary>
/// 出栈操作
/// </summary>
/// <returns></returns>
public T Pop()
{
if (_stackPoint > )
{
_stackPoint--;
return objStack[_stackPoint];
}
else
{
return objStack[];
} }
}
我们在 public class MyStack<T> 后面加了一个<T>这个时候这个类就变成了一个泛型类,表示一个占位符,当我们实例化该类的时候需要传入具体的数据类型,我们来看一下泛型类的具体用法:
public int[] arrayInt = new int[];
public string[] arrayStr = new string[];
MyStack<int> objMyIntStack = new MyStack<int>();
MyStack<string> objMyStrStack = new MyStack<string>();
这样泛型类就可以操作int 类型 和 string类型进行出栈入栈操作但代码却不需要改动。
三.泛型集合
使用泛型集合首先是是加了类型安全,方便编程,泛型集合指定了类型后只能将同类型的参数放入集合,泛型集合最常用就是List集合和Dictionary集合,我们分别看一下这两种集合。
A.Lis<T> 泛型集合
说到List泛型集合就不得不说ArrayList集合,ArrayList集合在操作是需要进行强制类型,极大的降低了代码处理效率所以,List集合应运而生,让我们看如下代码做个比较:
//用ArrayList集合来存储
ArrayList objArrayList = new ArrayList();
Students stu1 = new Students() { Name="小红",Age=};
Students stu2 = new Students() { Name = "小明", Age = };
objArrayList.Add(stu1);
objArrayList.Add(stu2);
Students obj = (Students)objArrayList[];
Console.WriteLine(obj.Name + obj.Age.ToString()); //这里需要进行强制类型转换
Console.ReadLine(); //用List集合来存储
List<Students> objList = new List<Students>()
{
new Students(){Name = "小红",Age=},
new Students(){Name="小明",Age = }
};
foreach (var item in objList)
{
Console.WriteLine(item.Name + "," + item.Age.ToString());
}
Console.ReadLine();
除此之外,我们一直在讲泛型集合可以保证数据安全,和ArrayList相比它的数据到底安全在什么地方呢,我们通过下面的例子做进一步说明:
ArrayList objArrayList = new ArrayList();
Students stu1 = new Students() { Name="小红",Age=};
Students stu2 = new Students() { Name = "小明", Age = }; Teacher tea1 = new Teacher() { Name = "小刚", Age = };
objArrayList.Add(stu1);
objArrayList.Add(stu2);
objArrayList.Add(tea1); //Teacher类也可以添加进来,类型不安全
foreach (var item in objArrayList)
{
Students obj00 = (Students)item;
}
从例子可以看出ArrayList集合的Add方法参数是object类型,所以Teacher的数据类型也可以放进去,这显然不是我们想要的,但是泛型集合就不一样,当占位符被确定的数据类型占用后,别的数据类型就添加不到集合中去。
List集合的常用方法,List集合中有很多方法,我们重点将一下Sort方法,Sort方法有四个重载方法,public void Sort();,public void Sort(Comparison<T> comparison);,
public void Sort(IComparer<T> comparer);,public void Sort(int index, int count, IComparer<T> comparer);我们直接调用Sort方法是按默认升序排序,假如某个类实现了IComparable接口那么默认排序就是按照接口中定义的方法来排序,看下面的例子:
//List集合排序
List<int> intList = new List<int>() { , , , , , , };
intList.Sort();
foreach (var item in intList)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
输出结果为:0,1,2,3......结果为升序排序
//字符串排序
List<string> objListStr = new List<string>() { "c","a","b"};
objListStr.Sort();
foreach (var item in objListStr)
{
Console.WriteLine(item);
}
Console.ReadLine();
输出结果为:a,b,c
假如是对象类型呢,默认的排序方法为升序排序,但是对象之间没有升序的概念,这个时候该怎么办呢,看下面的代码:
public class Students : IComparable<Students>
{
public string Name { get; set; }
public int Age { get; set; } /// <summary>
/// 实现泛型接口
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public int CompareTo(Students other)
{
return other.Name.CompareTo(this.Name);
}
} static void Main(string[] args)
{
Students stu1 = new Students() { Name = "Mick", Age = };
Students stu2 = new Students() { Name = "Jack", Age = };
List<Students> objList = new List<Students>();
objList.Add(stu1);
objList.Add(stu2);
objList.Sort();
foreach (var item in objList)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
Students类中实现了泛型接口IComparable<T> ,在泛型接口的方法中我们可以写排序的方式,这样做确实可以解决对象排序的问题,但是假如我们的排序条件是变化的,这种方式显然又不能满足我们的需求了,让我i们接着往下探索,如何实现集合对象的动态排序,让我们看如下代码:
/// <summary>
/// 按姓名降序排列
/// </summary>
public class NameDesc:IComparer<Students>
{ public int Compare(Students x, Students y)
{
return y.Name.CompareTo(x.Name);
}
} /// <summary>
/// 按姓名升序排序
/// </summary>
public class NameAsc : IComparer<Students>
{
public int Compare(Students x, Students y)
{
return x.Name.CompareTo(y.Name);
}
} /// <summary>
/// 按年龄降序
/// </summary>
public class AgeDesc:IComparer<Students>
{
public int Compare(Students x, Students y)
{
return y.Age - x.Age;
}
} /// <summary>
/// 按年龄升序
/// </summary>
public class AgeAsc : IComparer<Students>
{
public int Compare(Students x, Students y)
{
return x.Age.CompareTo(y.Age);
}
}
我们定义了一个自定义排序类,自定义排序类实现了ICompare接口。
static void Main(string[] args)
{
Students stu1 = new Students() { Name = "Mick", Age = };
Students stu2 = new Students() { Name = "Jack", Age = };
List<Students> objList = new List<Students>();
objList.Add(stu1);
objList.Add(stu2);
objList.Sort(new AgeDesc()); //基于接口实现多态的典型应用
foreach (var item in objList)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
调用List.Sort的重载方法,这里基于接口实现了多态,需要好好体会,关于集合的排序我们还可以用Linq查询。
B.Drictionary<> 泛型集合
List集合用索引查找元素的方法显然没有办法满足我们的实际需求,为了弥补这个缺陷,我们引入了字典的概念,说到键值对查询又不得不说说Hashtable,早期键值对集合都是用Hashtable类来实现的,后来泛型集合出现后Dictionary泛型集合取代了Hashtable类,让我们来看看两者的区别:
//用Hashtable集合
Hashtable objHashtable = new Hashtable();
objHashtable.Add("student1", new Students() { Name = "小王", Age = });
objHashtable.Add("student2", new Students() { Name = "小李", Age = });
Students stu =(Students)objHashtable["student1"]; //需要进行强制类型转换 //用Dictionary集合
Dictionary<string, Students> objDictionary = new Dictionary<string, Students>();
objDictionary.Add("student1", new Students() { Name = "小王", Age = });
objDictionary.Add("student2", new Students() { Name="小李",Age = });
Students myStudent = objDictionary["student1"]; //不需要进行强制类型转换
从例子可以看出Hashtable集合操作都是object的类型,在进行对象操作是需要进行强制类型转换,但是Dictionary却不一样,不需要进行强制类型转换,所以可以这样讲Dictionary出现以后可以完全替Hashtable。
四.泛型委托
A.自定义泛型委托
static void Main(string[] args)
{
Mydelegate<int> objMydelegate = Add;
Console.WriteLine("结果为:{0}", objMydelegate(, ));
Console.ReadLine();
}
static int Add(int i1,int i2)
{
return i1 + i2;
}
}
public delegate T Mydelegate<T>(T t1, T t2); //自定义泛型委托
以上例子就简单展示了自定泛型委托的使用方法,但是每次都这这么定义委托似乎很不方便,所以微软的工程师预先给我们定义好了几个泛型委托,我们可以直接使用,大大提高了使用泛型委托的便捷程度。
B.Func泛型委托的使用
Func是一个带返回值的泛型委托,func有多个重载版本,需要注意的是func最后一个参数是返回值类型,如果前面有泛型类型的参数,这个参数就是委托方法的形参类型,简单说func泛型委托就是一个带返回值的方法签名,我们先来看看它的简单应用:
static void Main(string[] args)
{
Func<int, int, int> objFunc = (a, b) => { return a + b; };
Console.WriteLine("结果为:{0}", objFunc(, ));
Console.ReadLine();
}
有人会说这样用似乎没什么意义,我们调用方法就可以直接实现功能,干嘛还要从委托转一下似乎多此一举,但是事实并不是如此,让我们看一下Func的复杂用法。现在提出一个需求,要求计算数组中任意指定开始位和结束位的“算数和” and 算数积。常规做法是:
static int GetSum(int[] nums, int from, int to)
{
int result = ;
for (int i = from; i <= to; i++)
{
result += nums[i];
}
return result;
}
static int GetMulti(int[] nums, int from, int to)
{
int result = ;
for (int i = from; i <= to; i++)
{
result *= nums[i];
}
return result;
}
写两个方法,分别计算和与积,但是还有别的实现方法么,答案是肯定的:
static void Main(string[] args)
{
int[] nums = { , , , , , , , , };
Console.WriteLine("数组前三个元素的和为:{0}", CommonMethod((a, b) => { return a + b; }, nums, , ));
Console.WriteLine("数组前三个元素的积为:{0}", CommonMethod((a, b) => { return a * b; }, nums, , ));
Console.ReadLine();
}
static int CommonMethod(Func<int, int, int> com, int[] nums, int a, int b)
{
int result = nums[a];
for (int i = a + ; i < b; i++)
{
result = com(result, nums[i]);
}
return result;
}
其实这里也体现了委托的本质,委托本来就是为了把方法当成参数传递而设计的。
C.Action泛型委托
Action泛型委托和func泛型委托差不多,只不过Action是不带返回值的方法的签名,看下面的例子我们就可以了解Action泛型委托的用法:
static void Main(string[] args)
{
Action<string> objAction = (a) => { Console.WriteLine(a); };
objAction("Hello C#");
Console.ReadLine();
}
D.Predicate泛型委托
Predicate<T>委托定义如下:
public delegate bool Predicate<T>(T obj);
解释:此委托返回一个bool值的方法
在实际开发中,Predicate<T>委托变量引用一个“判断条件函数”,
在判断条件函数内部书写代码表明函数参数所引用的对象应该满足的条件,条件满足时返回true
看下面的例子:
static void Main(string[] args)
{
List<int> objList = new List<int>() { , , , , , , , , }; List<int> resultList = objList.FindAll((s) => { return s > ; }); //Predicate委托 foreach (var item in resultList)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
好的以上就是关于泛型概念的总结,希望可以帮到有需要的人。
浅谈C#泛型的更多相关文章
- 浅谈Java泛型中的extends和super关键字(转)
通配符 在本文的前面的部分里已经说过了泛型类型的子类型的不相关性.但有些时候,我们希望能够像使用普通类型那样使用泛型类型: 向上造型一个泛型对象的引用 向下造型一个泛型对象的引用 向上造型一个泛型对象 ...
- 浅谈Java泛型之<? extends T>和<? super T>的区别
关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通. ...
- 浅谈Java泛型中的extends和super关键字
泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什 ...
- 浅谈Java——泛型DAO
首先解释一下为什么要学习泛型DAO.平时在写DAO的时候是一个接口对应一个实现类,实现类里面要写很多的操作数据库的方法.当我们有很多的javaben的时候我们会写很多的接口和实现类,并且里面的代码都是 ...
- 浅谈C#泛型的定义、继承、方法和约束
摘要:本文介绍了如何定义一个C#泛型类,以及实现泛型类的继承.方法和约束. C#泛型参数化了类型,把类型作为参数抽象出来,从而使我们在实际的运用当中能够更好的实现代码的重复利用,同时它提供了更强的类型 ...
- 转载--浅谈spring4泛型依赖注入
转载自某SDN-4O4NotFound Spring 4.0版本中更新了很多新功能,其中比较重要的一个就是对带泛型的Bean进行依赖注入的支持.Spring4的这个改动使得代码可以利用泛型进行进一步的 ...
- 浅谈Java泛型中的? extends E和?super E
https://blog.csdn.net/zymx14/article/details/78073757
- 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变
在net中json序列化与反序列化 准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...
- 浅谈开源项目Android-Universal-Image-Loader(Part 3.1)
本文转载于:http://www.cnblogs.com/osmondy/p/3266023.html 浅谈开源项目Android-Universal-Image-Loader(Part 3.1) 最 ...
随机推荐
- WPF C#之读取并修改App.config文件
原文:WPF C#之读取并修改App.config文件 简单介绍App.config App.config文件一般是存放数据库连接字符串的. 下面来简单介绍一下App.config文件的修改和更新. ...
- android该怎么办iphone那种画面抖动的动画效果(含有button和EditText)
首先在效果图: 要做到抖动效果按钮,能够这样做.设定anim房源res以下.创建一个button_shake.xml <? xml version="1.0" encodin ...
- XF 通过判断平台加载不同的图片
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- HALCON 光圈和景深的关系
光圈越大,越亮,景深越小 光圈越小,越暗,景深越大 景深为成像清晰的那个范围
- Qt 制作透明背景图片与裁剪图片(很实用)
这两天想做一个五子棋游戏,想从零开始自己绘制各种图片素材,将经验心得整理如下. 制作透明背景图片: void MyPainter::DrawKit() { QImage image(30, 30, Q ...
- git 专题
$ git pull origin test // git pull合并代码的时候,若发生冲突,会处于merging状态,检查代码,发现自己的分支低于主分支,这个时候想撤销merge // 撤销mer ...
- Ionic2开发环境搭建-VS 2017
原文:Ionic2开发环境搭建-VS 2017 目前在VS 2017中创建Ionic2版本项目 注:在VS中开发Ionic项目,使用的Ionic(v2.x),Cordova(v6.3.1),Angul ...
- Win8Metro(C#)数字图像处理--2.17图像木刻效果
原文:Win8Metro(C#)数字图像处理--2.17图像木刻效果 [函数名称] 图像木刻效果函数WoodCutProcess(WriteableBitmap src) [函数代码] ///& ...
- 你需要了解的 C++ 17 Top 19 新特性(附精彩评论)
什么是 C++17? C++17(或 C++1z)是继 C++14 之后 C++ 编程语言 ISO/IEC 标准的下一次修订的非正式名称.C++17 现在功能已齐全,正在成为国际标准的路上.它的规范已 ...
- WCF nginx反向代理遇到的问题
正常配置了nginx反向代理,其他java站点什么的都正常,就wcf总是失败.始终会跑如下异常: 由于 AddressFilter 在 EndpointDispatcher 不匹配,To 为“http ...