news 2026/5/12 17:51:06

C#基础9泛型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#基础9泛型

一、 泛型 :

允许我们延迟编写数据类型, 直到真正使用时确定类型的一种规范

方法本质上也是数据类型
泛型:体现多态,与var相似


1.可以创建自己的泛型接口、泛型类、泛型方法、泛型集合 ,泛型事件和泛型委托
2.泛型的格式: 结构<泛型的名称T>在定义泛型时T通常用作变量类型名称。但实际上 T 可以用任何有效名称代替

T是泛型默认的常用变量,但是也可以用其他大写字母代替
例如:名字<T>


3.泛型方法在定义是提供泛型的类型 在调用方法时 提供具体的延迟编写的类型
4.泛型无法直接使用运算符 比如 + - < > 等 但是能使用Object中的属性和方法

1、泛型方法:

有参泛型方法:

注意:泛型无法代替重载在于泛型不确定数据类型,无法直接使用运算符,但是泛型可以使用object类型中的全部东西


4.泛型无法直接使用运算符 比如 + - < > 等 但是能使用Object中的属性和方法

泛型中的类型数量和形参实参要一致
泛型方法调用中<>包括里面的内容可以省略(语法糖)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ZN_Lesson16_2泛型 { internal class Program { static void Main(string[] args) { People people = new People(); people.Sum(10, 20);//普通调用方法 //调用无参泛型方法 people.Sum<int>(); people.Sum<string>(); people.Sum<double>(); people.Sum<char>(); people.Sum<bool>(); people.Sum<object>(); people.Sum<decimal>(); people.Sum<byte>(); people.Sum<short>(); people.Sum<People>(); //调用有参泛型方法 people.Sum<int>(10, 20); people.Sum<string>("10","20"); people.Sum<double>(10, 20); people.Sum<object>(new object(), new object()); people.Sum<object>(new People(), new People());//类类型中参数可以写自身,也可以写其子类,但是子类不能写其父类 people.Sum<object>(1, 2); people.Sum<People>(new People(), new People()); people.Sum<People>(new Man(), new Man()); people.Sum<int>(20,20); people.Sum<int, float, double, string, bool>(1,1.0f,11.1,"123",true);//<>内的数据类型可以省略,但是不建议。同时,数据类型要与泛型方法中的数量一致。 } } public class Man :People { } public class People { //普通方法 public void Sum(int a ,int b) { Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(a+b); } public void Sum(float a, float b) { Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(a + b); } //无参泛型方法 public void Sum<T>() { } //有参泛型方法 public void Sum<T>(T a, T b) { Console.WriteLine(a); Console.WriteLine(b); // Console.WriteLine(a + b);//泛型方法中没有确定数据类型,会将a,b转化成object类型,object类型是引用类型,无法进行+运算 Console.WriteLine(a.ToString());//转字符串 Console.WriteLine(a.Equals(b));//判断是否相等 Console.WriteLine(a.GetType().ToString());//获取类型并转字符串 Console.WriteLine(b.GetType().ToString());//泛型方法可以使用object的全部功能 object obi1 = a; object obi2 = b;//真正明确a,b是object int i = (int)obi1; int j = (int)obi2;//拆箱为int类型进行+运算 Console.WriteLine(i+j); } public void Sum<T,T1,T2,A,中>(T a, T1 b,T2 c,A d,中 e)//泛型方法的数据类型数量要一致 { Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(c); } } }

2、泛型类

C# 语言中泛型类的定义与泛型方法类似,是在泛型类的名称后面加上<T>,当然,也可以定

义多个类型,即“<T1,T2,・・・>”。

//Main方法 Person person = new Person("张三", 18); person.Show(1); Person.Show(); Person<float> person1 = new Person<float>("李四",10.0f); Console.WriteLine(person1.Age.GetType()); Person<double> person2 = new Person<double>("王五",10.0); Console.WriteLine(person2.Age.GetType()); StringBuilder sb = new StringBuilder();//stringBuilder可变字符串类 sb.Append("张三");//可变字符串追加内容 int a = 10; Person<StringBuilder,int> person3 = new Person<StringBuilder,int>(sb,a); person3.Name.Append("张三1111"); //泛型类 public class Person<T> { private string _name; public string Name { get { return _name; } set { _name = value; } } public T _age; public T Age { get { return _age; } set { _age = value; } } public Person(string name, T age) { this.Name = name; this.Age = age; } public static void Show() { Console.WriteLine("静态Show"); } public void Show(int a) { Console.WriteLine("实例Show{0}", a); } } public class Person<T,T1> { private T _name; public T Name { get { return _name; } set { _name = value; } } public T1 _age; public T1 Age { get { return _age; } set { _age = value; } } public Person(T name, T1 age) { this.Name = name; this.Age = age; } public static void Show() { Console.WriteLine("静态Show"); } public void Show(T a) { Console.WriteLine("实例Show{0}", a); Console.WriteLine(a.GetType()); } }

3、泛型接口

接口为类提供语法合同,接口泛型为接口实现多态

接口泛型在继承时就要确定数据类型

interface Inter<T> { void show(T t); } //定义接口Inter的子类InterImpA,明确泛型类型为String (2) public class InterImpA : Inter<int> { //子类InterImpA重写方法show,指明参数类型为string public void show(int t) { Console.WriteLine(t); } } //定义接口Inter的子类InterImpB,直接声明 (1) public class InterImpB<T> : Inter<T> { public void show(T t) { Console.WriteLine(t); } }
泛型和Object类型的区别 值类型转换成引用类型。这个转换称为装箱。相反的过程称为拆箱 int number = 10; // 装箱 object obj = number; // 拆箱 number = (int) obj; C# 中 Object 是一切类型的基类,可以用来表示所有类型 Object 类型 > 优点: > 1. object类型可以用来引用任何类型的实例; > 2. object类型可以存储任何类型的值; > 3. 可以定义object类型的参数; > 4. 可以把object作为返回类型。 > 缺点: > 1. 会因为程序员没有记住使用的类型而出错,造成类型不兼容; > 2. 值类型和引用类型的互化即装箱拆箱使系统性能下降 装箱(从值类型转换到引用类型)需要经历如下几个步骤: int a =10 object ob = a; 首先在堆上分配内存。这些内存主要用于存储值类型的数据。 接着发生一次内存拷贝动作,将当前存储位置的值类型数据拷贝到堆上分配好的位置。 最后返回对堆上的新存储位置的引用。 拆箱(从引用类型转换为值类型)的步骤则相反: // 拆箱 number = (int) obj; 首先检查已装箱的值的类型兼容目标类型。 接着发生一次内存拷贝动作,将堆中存储的值拷贝到栈上的值类型实例中。 最后返回这个新的值。 频繁拆装箱导致性能问题 由于拆箱和装箱都会涉及到一次内存拷贝动作,因此频繁地进行拆装箱会大幅影响性能 泛型和装箱拆箱 对比 public void abTest(object a, object b) { Console.WriteLine(a.ToString()); Console.WriteLine(b.ToString()); } public void abTest(T a, T b) { Console.WriteLine(a.ToString()); Console.WriteLine(b.ToString()); }

4、泛型约束

泛型中的数据约束可以指定泛型类型的范围

泛型约束总共有五种。
约束 说明
T:结构 类型参数必须是值类型
T:类 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。
where T :struct//约束 T 必须是值类型
where K :class//约束 K 必须是引用类型
where V :IFace<T>//约束 V 必须实现IFace 接口 ,包括继承接口的类
where W :K//约束 W 必须是K 类型,或者是K 类型的子类
where X :class, new()//约束 X 必须是引用类型,并且有一个无参数的构造函数,当有多个约束时,new()必须写在最后
where Y :
MyClass2//约束 Y 必须是MyClass2 类型,或者继承于 MyClass2 类

泛型约束总共有五种。 约束 说明 T:结构 类型参数必须是值类型 T:类 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 T:new() 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。 T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。 T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。 where T : struct //约束 T 必须是值类型 where K : class //约束 K 必须是引用类型 where V : IFace<T> //约束 V 必须实现 IFace 接口 where W : K //约束 W 必须是 K 类型,或者是 K 类型的子类 where X : class, new() //约束 X 必须是引用类型,并且有一个无参数的构造函数,当有多个约束时,new()必须写在最后 where Y : MyClass2 //约束 Y 必须是 MyClass2 类型,或者继承于 MyClass2 类 //例子: internal class Program { static void Main(string[] args) { Person<int,string,IFaceClass,string> zhangsan = new Person<int,string,IFaceClass,string>(); zhangsan.Age = 10; Person<float,Program,IFaceClass,Program> zhangsan1 = new Person<float,Program,IFaceClass,Program>(); zhangsan1.Age = 10.0f; Person<double, People, IFaceClass, Man> zhangsan2 = new Person<double, People, IFaceClass, Man>(); zhangsan1.Age = 10.0f; } } public class People:IComparable<People>{ public int CompareTo(People other) { return 1; } } public class Man : People { } public interface IFace<T> { void IFaceMothod(); } public class IFaceClass:IFace<int> ,IFace<float>,IFace<double>{ public void IFaceMothod() { } } public class Person <T,K,V,W> where T : struct where K : class where V : IFace<T> where W : K { private T _age; public T Age { get { return _age; } set { _age = value; } } public K TesT { get; set; } } 委托约束 枚举约束 后续再说

5、泛型默认值问题,dafault()方法

之所以会用到default关键字,是因为需要在不知道类型参数为值类型还是引用类型的情况下,为对象实例赋初值。 引用类型会返回 null,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或 null 的每个结构成员,具体取决于这些结构是值类型还是引用类型。对于可以为 null 的值类型,默认返回 System.Nullable<T> ///如果我们用int型来绑定泛型参数,那么T就是int型, ///那么注释的那一行就变成了 int t = null;显然这是无意义的。 ///为了解决这一问题,引入了default关键字: TestDefault<int?> testDefault = new TestDefault<int?>(); Console.WriteLine(testDefault.foo()); class TestDefault<T> { public T foo() { // T t = null; //错误赋值方式 T t = default(T); return t; } }

二、List泛型集合

List 与其他数组的比较:
List与静态数组(Array类)比较类似,都用于存放一组相同类型的值。
List与动态数组(ArrayList)比较类似 都是元素长度不固定 。即List泛型类型固定,长度不固定

using System;

// 泛型集合的命名空间
using System.Collections.Generic;

using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ZN_Lesson16_3_泛型集合
{
internal class Program
{
static void Main(string[] args)
{
//存放一组相同类型的值
//泛型的代表存放的元素类型

// 创建了一个List的对象
List<string> list = new List<string>();
list.Add("张三");
list.Add("王五");//list.Add();中的Add有添加和开辟list泛型集合空间的作用

//通过索引访问

string str1 = list[0];
//修改元素
list[0] = "赵六";

//循环添加
for (int i = 1; i <= 10; i++)
{
list.Add(i.ToString());
}


//范围添加元素
//AddRange(IEnumerable<T> collection) collection 可以传入继承IEnumerable<T>的集合对象
list.AddRange(list);

//遍历元素
foreach (var item in list)
{
Console.WriteLine(item);
}



for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);

//注意长度变化 容易出现无限循环
// list.Add("小王");

Console.WriteLine(list.Count);
}

//插入元素
list.Insert(0, "a");
list.IndexOf("a");
//删除元素 第一个匹配项
list.Remove("a");
//删除元素 按索引
list.RemoveAt(0);
//删除元素 按索引范围
list.RemoveRange(0, 5);
//包含某元素
list.Contains("a");
//清空元素
list.Clear();
//搜索所有匹配项
list.FindAll(x => x == "a");
//删除所有匹配项
//list.RemoveAll(x => x == "a");
}
}
}

ArrayList与List<T>


不可变数组和list<>集合只能存一种固定·类型,但是ArrayList可以存储任意类型(实质上存储的object类型,存在装箱拆箱操作)

抽象类继承接口时,因为抽象类和接口不存在实现功能,所以,继承接口的功能在抽象类的子类中进行实现功能
Array类是所有不可变数组的基类


ArrayList的优点:

ArrayList大小是按照其中存储的数据来动态扩充与收缩的 长度不固定

ArrayList可以很方便地进行数据的添加插入删除

ArrayList 可以存储任意类型

ArrayList的缺点:

ArrayList在存储数据时使用object类型进行存储的

ArrayList不是类型安全的,使用时很可能出现类型不匹配的错误
就算都插入了同一类型的数据,使用时我们也需要将它们转化为对应的原类型来处理

ArrayList 存储在装箱和拆箱操作,导致其性能低下

using System.Collections; //ArrayList所在命名空间

ArrayList aList = new ArrayList();
//插入
aList.Add(123);
aList.Add("ABC");
aList.Insert(1, 123 + "ABC"); //123 + "ABC" ->(隐式转换)"123ABC"
//移除
aList.RemoveAt(0); //索引处
aList.Remove("ABC");

using System; // 引用 ArrayList类所在的命名空间 using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ZN_LessonDay19_1_ArrayList { internal class Program { static void Main(string[] args) { // 创建ArrayList对象 ArrayList arrayList = new ArrayList(); // 添加元素 arrayList.Add("张三"); arrayList.Add(1); arrayList.Add(11.0f); arrayList.Add(true); arrayList.Add(new Person()); arrayList.Add(new object()); arrayList.Add(new int[5]); arrayList.Add(new List<string>()); arrayList.Add(arrayList); //添加多个元素 arrayList.AddRange(arrayList); //添加不可变数组 arrayList.AddRange(new string[] { "张三", "张三", "张三" }); arrayList.AddRange(new List<string>() {"1","2"}); arrayList.AddRange(Array.CreateInstance(typeof(int), 5)); //总结 : 接口作为参数类型时,可以传入实现了该接口的任意对象 //获取集合的长度 Console.WriteLine(arrayList.Count); //读取集合中的元素 根据索引值 读取集合中的元素 //元素类型都为 object object ob1 = arrayList[1]; //值类型拆箱 int num = (int)ob1; //引用类型 类型转换 object ob4 = arrayList[4]; Person p1 = (Person)ob4; p1.Name = "张三"; //Object类型 转换成子类时 //在编译阶段不会报错 但是如果类型不一致 在运行阶段就会报错 //object ob8 = arrayList[8]; //Person p2 = (Person)ob8; //p2.Name = "张三"; //修改数组元素 arrayList[8] = new Person(); object ob8 = arrayList[8]; Person p2 = (Person)ob8; p2.Name = "张三"; Console.WriteLine($"删除前的长度{arrayList.Count}"); //删除数组元素 //根据索引值删除元素 arrayList.RemoveAt(8); //根据对象删除元素 arrayList.Remove(p2); Console.WriteLine($"删除后的长度{arrayList.Count}"); //遍历数组元素 //for (int i = 0; i < arrayList.Count; i++) //{ // Console.WriteLine(arrayList[i]); //} foreach (object item in arrayList) { Console.WriteLine(item); } Console.ReadKey(); } } public class Person { public string Name { get; set;} } }

C#中的Dictionary字典类

字典也是一个泛型集合,与list<>在一个命名空间

字典无序没有索引
获取字典的元素,通过字典唯一的键Key获取,不再用下标获取

特点:

必须包含名空间System.Collection.Generic
Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值)
键必须是唯一的,而值不需要唯一的

字典 长度是不固定的 随着元素增减 而改变
键和值都可以是任何类型(比如:string, int, 自定义类型,等等)

常用属性

名称 说明
Comparer 获取用于确定字典中的键是否相等的 IEqualityComparer<T>。
Count 获取包含在 Dictionary<TKey, TValue> 中的键/值对的数目。
Item 获取或设置与指定的键相关联的值。
Keys 获取包含 Dictionary<TKey, TValue> 中的键的集合。
Values 获取包含 Dictionary<TKey, TValue> 中的值的集合。

常用方法


名称 说明
Add 将指定的键和值添加到字典中。
Clear 从 Dictionary<TKey, TValue> 中移除所有的键和值。
ContainsKey 确定 Dictionary<TKey, TValue> 是否包含指定的键。
ContainsValue 确定 Dictionary<TKey, TValue> 是否包含特定值。

Remove 从 Dictionary<TKey, TValue> 中移除所指定的键的值。
ToString 返回表示当前对象的字符串。 (继承自 Object。)


//初始化字典
Dictionary<string,int> dic =new Dictionary<string, int>();
//1 添加元素
dic.Add("1",1);
dic.Add("2",2);
dic.Add("3",3);
dic.Add("4",4);
dic.Add("abc",53432432);
dic.Add("6",1);
//错误 key值重复
// dic.Add("6", 2);//字典中,key值是唯一的,不能重复

//2.获取元素
Console.WriteLine(dic["abc"]);
//无法获取不存在key值
// Console.WriteLine(dic["333"]);
//3.修改元素
dic["abc"] = 10;
//如果key值不存在,则添加 如果key值存在,则修改
dic["333"] = 333;

//4.删除元素
dic.Remove("abc");
//5.清空元素
// dic.Clear();
//利用取巧的方法遍历字典
Dictionary<int,string> dic1 = new Dictionary<int,string>();

for (int i = 0; i < 10; i++)
{
dic1.Add(i, i.ToString());
}

// dic1.Add(111, "111");//但是取巧的for循环中间不能添加东西

for (int i = 0; i < dic1.Count; i++)
{
Console.WriteLine(dic1[i]);
Console.WriteLine(dic1[i].GetType());
}
Console.WriteLine("----------------");
//6.遍历字典的keys集合
foreach (var item in dic1.Keys)
{
Console.WriteLine(item);
}
Console.WriteLine("------------------------");
//7.遍历字典的values集合
foreach (var item in dic1.Values)
{
Console.WriteLine(item);
}

//8.通过key遍历字典value
foreach (var item in dic1.Keys)
{
Console.WriteLine(dic1[item]);
}

//9 通过遍历字典 获取key和value
foreach (KeyValuePair<int,string> item in dic1)
{
//获取key
Console.WriteLine(item.Key);
//获取value
Console.WriteLine(item.Key);
Console.WriteLine(dic1[item.Key]);
}
}

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp33 { internal class Program { static void Main(string[] args) { //字典的对象 Dictionary<string,int> dic = new Dictionary<string,int>(); Dictionary<int,string> dic1 = new Dictionary<int,string>(); //添加元素 dic1.Add(0, "a"); dic1.Add(1, "b"); dic1.Add(2, "c"); dic.Add("1", 123); dic.Add("2", 333); dic.Add("abc", 333); dic.Add("3", 456); //查找 Console.WriteLine(dic1[0]); Console.WriteLine(dic["3"]); //修改 dic1[2] ="d"; dic["2"] = 444; for (int i = 0; i < dic1.Count; i++) { Console.WriteLine(dic1[i]); } //dic.Keys 集合 foreach (string item in dic.Keys) { Console.WriteLine(dic[item]); } // dic.Values 集合 foreach (int item in dic.Values) { Console.WriteLine(item); } // foreach (KeyValuePair<string,int> item in dic) { Console.WriteLine(item.Key); Console.WriteLine(item.Value); } ////移除元素 bool isRmove = dic.Remove("aaadsasdasd"); if (isRmove) { Console.WriteLine("删除成功"); } else { Console.WriteLine("删除失败"); } // //判断字典中是否包含某个key bool isTrue = dic.ContainsKey("2"); if (isTrue) { dic.Remove("2"); } else { dic.Add("2",11); } // //清空 dic.Clear(); // //字典中key/value的个数 Console.WriteLine(dic.Count); Console.ReadKey(); } } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 17:50:05

【新手避坑】Keil5从零到一:手把手搭建你的第一个STM32工程

1. 为什么选择Keil5开发STM32&#xff1f; 第一次接触STM32开发的朋友&#xff0c;肯定会被各种开发环境搞得眼花缭乱。我刚开始学的时候也纠结过&#xff0c;到底用IAR、Keil还是直接用VSCode插件&#xff1f;后来发现Keil MDK&#xff08;也就是常说的Keil5&#xff09;是最适…

作者头像 李华
网站建设 2026/5/12 17:47:52

如何在Photoshop中解锁下一代图像格式AVIF的强大能力?

如何在Photoshop中解锁下一代图像格式AVIF的强大能力&#xff1f; 【免费下载链接】avif-format An AV1 Image (AVIF) file format plug-in for Adobe Photoshop 项目地址: https://gitcode.com/gh_mirrors/avi/avif-format 还在为网页图片加载速度慢而烦恼吗&#xff1…

作者头像 李华
网站建设 2026/5/12 17:46:39

android 最常用加密策略与开源库 sha256

采用sha256是最常用的&#xff0c;而这个库是最常用的&#xff1a;implementation com.google.guava:guava:32.1.2-android用起来非常简单&#xff1a;String pwd Hashing.sha256().hashString(明文密码, StandardCharsets.UTF_8).toString();这就是加密以后得数值

作者头像 李华