IT评测·应用市场-qidao123.com

标题: 第四单元 泛型 [打印本页]

作者: 八卦阵    时间: 2023-6-13 12:02
标题: 第四单元 泛型
1. 什么是泛型

编写一个方法,实现两数相加并返回结果。

 
作用
 
定义
泛型是可以当作任意一种且由编译期间决定其最终类型的数据类型。通俗来讲,泛型,即泛指某种类型。
 
2. 泛型类

1. 泛型类声明格式

泛型类,将指定类型参数(Type Parameter,通常以T 表示),紧随类名,并包含在符号内。
  1. public class 泛型类<T>
  2. {
  3.     /// <summary>
  4.     /// 泛型属性
  5.     /// </summary>
  6.     public T ItemName { get; set; }
  7.     public string MyName { get; set; } // 也可定义其他的属性
  8.    
  9. }
复制代码
使用泛型类
  1. 泛型类<string> obj = new();
  2. obj.ItemName = "任我行码农场";
  3. Console.WriteLine("ItemName的值是:"+obj.ItemName);
  4. Console.WriteLine("ItemName的类型是:"+obj.ItemName.GetType());
复制代码
输出结果:
  1. ItemName的值是:任我行码农场
  2. ItemName的类型是:System.String
复制代码
3. 泛型方法

泛型方法,将指定类型参数(Type Parameter,通常以T 表示),紧随方法名,并包含在符号内。
格式
  1. 访问修饰符  方法返回类型   方法名<T>(参数列表)
  2. {
  3.         // 方法体...
  4. }
复制代码
  
普通类中的泛型
  1. public class MyClass
  2. {
  3.     // 泛型方法
  4.     public T Sum<T>(T a, T b)
  5.     {
  6.         return (dynamic) a + b;
  7.     }
  8. }
复制代码
  
泛型类中的泛型方法
  1. public class 泛型类<T>
  2. {
  3.     /// <summary>
  4.     /// 泛型属性
  5.     /// </summary>
  6.     public T ItemName { get; set; }
  7.     public string MyName { get; set; }
  8.    
  9.     public void Sum<T>(T a, int b)
  10.     {
  11.         Console.WriteLine((dynamic)a+b);
  12.     }
  13.    
  14. }
复制代码
 
4. 泛型约束

1. 为什么要用泛型约束
  1. [Test]
  2. public void Test2()
  3. {
  4.     MyClass my = new MyClass();
  5.     Student s1 = new Student(1,"张三");
  6.     Student s2 = new Student(2,"李四");
  7.     my.Sum<Student>(s1, s2); // 合适吗?
  8. }
  9. record Student(int Id,string Name);
复制代码
 
上述代码一定会报错, 两个Student对象不可能可以直接相加!!
此时,如果不对Sum 这个泛型方法加以约束,就很有可能出现上述情况。
所谓泛型约束,实际上就是约束的类型T。使T必须遵循一定的规则。比如T必须继承自某个类或者T必须实现某个接口等。 使用where关键字加上约束
格式如下:
  1. public class 泛型类<T> where T:约束类型
  2. {
  3.    
  4. }
复制代码
  
2. 约束的类型

struct类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型class类型参数必须是引用类型,包括任何类、接口、委托或数组类型。new()类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。基类名类型参数必须是指定的基类或派生自指定的基类接口名类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 
泛型约束--struct
泛型约束中的struct 指定类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型
  1. public class MyClass
  2. {
  3.     // 泛型方法
  4.     public T Sum<T>(T a, T b) where T:struct
  5.     {
  6.         return (dynamic) a + b;
  7.     }
  8. }
  9. [Test]
  10. public void Test2()
  11. {
  12.     MyClass my = new MyClass();
  13.     Student s1 = new Student(1,"张三");
  14.     Student s2 = new Student(2,"李四");
  15.     my.Sum<Student>(s1, s2); // 此时编译器直接给出错误提示,编译失败
  16. }
  17. record Student(int Id,string Name);
复制代码
 
my.Sum(s1, s2); // 此时编译器直接给出错误提示,编译失败
 
泛型约束--class
泛型约束class ,指定类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
  1. public interface IRepository<T> where T:class
  2. {
  3.     // 接口也可以有默认实现
  4.     int Add(T model)
  5.     {
  6.         Console.WriteLine("添加了一条数据");
  7.         return 1;
  8.     }
  9.     int Update(T model);
  10.     int Delete(dynamic id);
  11.     T GetModel(dynamic id);
  12.     IList<T> GetList(string condition);
  13. }
复制代码
 
如果有组合约束时,class约束必须放在最前面。
  1. public interface IRepository<T> where T:class,new() // class放前面,否则编译失败
  2. {
  3.     int Add(T model);
  4.     int Update(T model);
  5.     int Delete(dynamic id);
  6.     T GetModel(dynamic id);
  7.     IList<T> GetList(string condition);
  8. }
复制代码
 
测试效果
  1. IRepository<int> repository = new IRepository<int>(); // 编译失败
  2. IRepository<object> repository = new IRepository<object>(); // 编译通过
复制代码
 
泛型约束—new()
泛型约束new(),指定类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。加上该约束后可以在类中或者方法中实例化T类型的对象。
  1. public class BaseDAL<T> where T:class,new() //new()放后面
  2. {
  3.     public List<T> GetList<T>()
  4.     {
  5.         List<T> list = new();
  6.         T t = new(); // 可以实例化了
  7.         list.Add(t);
  8.         
  9.         return list;
  10.     }
  11. }
复制代码
 
测试效果
  1. BaseDAL<Student> dal = new BaseDAL<Student>(); // 编译失败,Student并未提供无参构造
  2. record Student(int Id,string Name);
复制代码
  
泛型约束—基类名
类型约束之基类名称,类型参数必须是指定的基类或派生自指定的基类
  1. public class StudentDal<T> where T:BaseModel
  2. {
  3.    
  4. }
  5. class BaseModel
  6. {
  7.    
  8. }
复制代码
  
说明:基类约束时,基类不能是密封类,即不能是sealed类。sealed类表示该类不能被继承,在这里用作约束就无任何意义,因为sealed类没有子类.
 
泛型约束—接口名称
泛型约束之接口名称,类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
  1. interface IAnimal
  2. {
  3.     // ...
  4. }
  5. interface IPerson
  6. {
  7.     // ...
  8. }
  9. class 泛型类<T> where T:IAnimal,IPerson
  10. {
  11.    
  12. }
  13. class Student:IAnimal,IPerson
  14. {
  15.    
  16. }
  17. // 测试使用
  18. 泛型类<Student> myClass = new 泛型类<Student>(); // 测试通过
复制代码
  
5. 泛型协变和逆变

协变(Convariant)和逆变(Contravariant)的出现,使数组、委托、泛型类型的隐式转换变得可能。 子类转换成基类,称之为协变;基类转换成子类,称之为逆变。.NET4.0以来,支持了泛型接口的协变和逆变。
 
泛型协变

如果子类泛型隐式转换成基类泛型,使用泛型协变
 
  1. public class FactoryImpl<T>:IFactory<T> where T:new()
  2. {
  3.     public T Create()
  4.     {
  5.         return new T();
  6.     }
  7. }
复制代码
 
 
 

 
泛型逆变

如果基类泛型隐式转换成子类泛型,使用泛型逆变。
 
● INotifier的方法Notify()的参数类型是INotification,逆变后把INotification类型参数隐式转换成了实现类 MailNotificaiton。 ● 泛型接口中的in关键字必不可少
 
协变逆变总结

逆变与协变只能放在泛型接口和泛型委托的泛型参数里面,在泛型中out修饰泛型称为协变,协变(covariant) 修饰返回值 ,协变的原理是把子类指向父类的关系,拿到泛型中。
在泛型中in 修饰泛型称为逆变, 逆变(contravariant )修饰传入参数,逆变的原理是把父类指向子类的关系,拿到泛型中。
 
内置的协变逆变泛型
序号类型名称1接口IEnumerable2委托Action3委托Func4接口IReadOnlyList5接口IReadOnlyCollection 
6. 泛型的应用

手写ORM框架

ORM 框架,对象关系映射。
DataTable 转 List

DataTable 转换成List
  1. private static List<T> ToList<T>(DataTable dt) where T : class, new()
  2. {
  3.     Type t = typeof(T);
  4.     PropertyInfo[] propertys = t.GetProperties();
  5.     List<T> lst = new List<T>();
  6.     string typeName = string.Empty;
  7.     foreach (DataRow dr in dt.Rows)
  8.     {
  9.         T entity = new T();
  10.         foreach (PropertyInfo pi in propertys)
  11.         {
  12.             typeName = pi.Name;
  13.             if (dt.Columns.Contains(typeName))
  14.             {
  15.                 if (!pi.CanWrite) continue;
  16.                 object value = dr[typeName];
  17.                 if (value == DBNull.Value) continue;
  18.                 if (pi.PropertyType == typeof(string))
  19.                 {
  20.                     pi.SetValue(entity, value.ToString(), null);
  21.                 }
  22.                 else if (pi.PropertyType == typeof(int) ||
  23.                          pi.PropertyType == typeof(int?))
  24.                 {
  25.                     pi.SetValue(entity, int.Parse(value.ToString()), null);
  26.                 }
  27.                 else if (pi.PropertyType == typeof(DateTime?) ||
  28.                          pi.PropertyType == typeof(DateTime))
  29.                 {
  30.                     pi.SetValue(entity, DateTime.Parse(value.ToString()), null);
  31.                 }
  32.                 else if (pi.PropertyType == typeof(float))
  33.                 {
  34.                     pi.SetValue(entity, float.Parse(value.ToString()), null);
  35.                 }
  36.                 else if (pi.PropertyType == typeof(double))
  37.                 {
  38.                     pi.SetValue(entity, double.Parse(value.ToString()), null);
  39.                 }
  40.                 else
  41.                 {
  42.                     pi.SetValue(entity, value, null);
  43.                 }
  44.             }
  45.         }
  46.         lst.Add(entity);
  47.     }
  48.     return lst;
  49. }
复制代码
配套视频链接:
C# 高级编程,.Net6 系列 开发第三阶段,学完拿捏你的面试官,.net6 进阶学习(已完结)_哔哩哔哩_bilibili
  
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4