首页 > 其他分享 >第四单元 泛型

第四单元 泛型

时间:2023-06-13 10:12:00浏览次数:34  
标签:接口 第四 public 泛型 new where class 单元

1. 什么是泛型

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

 

作用

  1. 泛型增强了代码的可读性

  2. 泛型有助于实现代码的重用、保护类型的安全以及提高性能。

  3. 我们可以创建泛型集合类。

  4. 泛型实现了类型和方法的参数化

  5. 我们还可以对泛型类进行约束以访问特定数据类型的方法。

  6. 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

 

定义

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

 

2. 泛型类

1. 泛型类声明格式

泛型类,将指定类型参数(Type Parameter,通常以T 表示),紧随类名,并包含在<>符号内。

public class 泛型类<T>
{
    /// <summary>
    /// 泛型属性
    /// </summary>
    public T ItemName { get; set; }

    public string MyName { get; set; } // 也可定义其他的属性
    
}

使用泛型类

泛型类<string> obj = new();
obj.ItemName = "任我行码农场";

Console.WriteLine("ItemName的值是:"+obj.ItemName);
Console.WriteLine("ItemName的类型是:"+obj.ItemName.GetType());

输出结果:

ItemName的值是:任我行码农场
ItemName的类型是:System.String

3. 泛型方法

泛型方法,将指定类型参数(Type Parameter,通常以T 表示),紧随方法名,并包含在<>符号内。

格式

访问修饰符  方法返回类型   方法名<T>(参数列表)
{
	// 方法体...
}

  

普通类中的泛型

public class MyClass
{
    // 泛型方法
    public T Sum<T>(T a, T b)
    {
        return (dynamic) a + b;
    }
}

  

泛型类中的泛型方法

public class 泛型类<T>
{
    /// <summary>
    /// 泛型属性
    /// </summary>
    public T ItemName { get; set; }

    public string MyName { get; set; }

    
    public void Sum<T>(T a, int b)
    {
        Console.WriteLine((dynamic)a+b);
    }
    
}

 

4. 泛型约束

1. 为什么要用泛型约束

[Test]
public void Test2()
{
    MyClass my = new MyClass();
    Student s1 = new Student(1,"张三");
    Student s2 = new Student(2,"李四");
    my.Sum<Student>(s1, s2); // 合适吗?
}

record Student(int Id,string Name);

 

上述代码一定会报错, 两个Student对象不可能可以直接相加!!

此时,如果不对Sum 这个泛型方法加以约束,就很有可能出现上述情况。

所谓泛型约束,实际上就是约束的类型T。使T必须遵循一定的规则。比如T必须继承自某个类或者T必须实现某个接口等。 使用where关键字加上约束

格式如下:

public class 泛型类<T> where T:约束类型
{
   
}

  

2. 约束的类型

struct类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型
class 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
基类名 类型参数必须是指定的基类或派生自指定的基类
接口名 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

 

泛型约束--struct

泛型约束中的struct 指定类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型

public class MyClass
{
    // 泛型方法
    public T Sum<T>(T a, T b) where T:struct
    {
        return (dynamic) a + b;
    }
}

[Test]
public void Test2()
{
    MyClass my = new MyClass();
    Student s1 = new Student(1,"张三");
    Student s2 = new Student(2,"李四");
    my.Sum<Student>(s1, s2); // 此时编译器直接给出错误提示,编译失败
}

record Student(int Id,string Name);

 

my.Sum<Student>(s1, s2); // 此时编译器直接给出错误提示,编译失败

 

泛型约束--class

泛型约束class ,指定类型参数必须是引用类型,包括任何类、接口、委托或数组类型。

public interface IRepository<T> where T:class
{
    // 接口也可以有默认实现
    int Add(T model)
    {
        Console.WriteLine("添加了一条数据");
        return 1;
    }

    int Update(T model);

    int Delete(dynamic id);

    T GetModel(dynamic id);

    IList<T> GetList(string condition);
}

 

如果有组合约束时,class约束必须放在最前面。

public interface IRepository<T> where T:class,new() // class放前面,否则编译失败
{
    int Add(T model);

    int Update(T model);

    int Delete(dynamic id);

    T GetModel(dynamic id);

    IList<T> GetList(string condition);
}

 

测试效果

IRepository<int> repository = new IRepository<int>(); // 编译失败

IRepository<object> repository = new IRepository<object>(); // 编译通过

 

泛型约束—new()

泛型约束new(),指定类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。加上该约束后可以在类中或者方法中实例化T类型的对象。

public class BaseDAL<T> where T:class,new() //new()放后面
{
    public List<T> GetList<T>()
    {
        List<T> list = new();
        T t = new(); // 可以实例化了
        list.Add(t);
        
        return list;
    }
}

 

测试效果

BaseDAL<Student> dal = new BaseDAL<Student>(); // 编译失败,Student并未提供无参构造

record Student(int Id,string Name);

  

泛型约束—基类名

类型约束之基类名称,类型参数必须是指定的基类或派生自指定的基类

public class StudentDal<T> where T:BaseModel
{
    
}

class BaseModel
{
    
}

  

说明:基类约束时,基类不能是密封类,即不能是sealed类。sealed类表示该类不能被继承,在这里用作约束就无任何意义,因为sealed类没有子类.

 

泛型约束—接口名称

泛型约束之接口名称,类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

interface IAnimal
{
    // ...
}

interface IPerson
{
    // ...
}


class 泛型类<T> where T:IAnimal,IPerson
{
    
}

class Student:IAnimal,IPerson
{
    
}

// 测试使用
泛型类<Student> myClass = new 泛型类<Student>(); // 测试通过

  

5. 泛型协变和逆变

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

 

泛型协变

如果子类泛型隐式转换成基类泛型,使用泛型协变

 

  1. 先准备好两个子父类

    public class Animal
    {
        public virtual void Run()
        {
            Console.WriteLine("动物在跑");
        }
    }
    
    public class Dog:Animal
    {
        public override void Run()
        {
            Console.WriteLine("狗在跑");
        }
    }

     

  2. 定义好泛型协变接口,

    public interface IFactory<out T> // out 协变 只能应用于interface
    {
        T Create(); 
    }
    
    
  3. 实现协变接口
public class FactoryImpl<T>:IFactory<T> where T:new()
{
    public T Create()
    {
        return new T();
    }
}

 

 

 

  1. 测试泛型协变

    [Test]
    public void Test3()
    {
        IFactory<Dog> iFactory = new FactoryImpl<Dog>();
        IFactory<Animal> parentFactory = iFactory; // 协变
    
        Animal animal = parentFactory.Create();
        animal.Run();// 输出结果:狗在跑
    }
  • 泛型接口中的out关键字必不可少

  • out 协变 只能应用于interface

 

泛型逆变

如果基类泛型隐式转换成子类泛型,使用泛型逆变。

 

  1. 关于通知的一个接口

    public interface INotification
    {
        public string Message { get; }
    }
    
    // 关于通知接口的抽象实现。
    public abstract class Notification : INotification
    {
        public abstract string Message { get; }
    }
  2. 关于通知抽象类的具体实现。

    public class MainNotification : Notification
    {
        public override string Message => "您有一封新的邮件";
    }
  3. 接下来,需要把通知的信息发布出去,需要一个发布通知的接口INotifier,该接口依赖INotification,大致INotifier<INotification>,而最终显示通知,我们希望INotifier<MailNotification>,INotifier<INotification>转换成INotifier<MailNotification>,这是逆变,需要关键字in。

    public interface INotifier<in T> where T : INotification
    {
        void Notifier(T notification);
    }

     

  4. 实现INotifier:

    public class Notifier<T> : INotifier<T> where T : INotification
    {
        public void Notify(T notification)
        {
            Console.WriteLine(notification.Message);
        }
    }

     

  5. 客户端调用

    [Test]
    public void Test4()
    {
        INotifier<INotification> notifier = new Notifier<INotification>();
        INotifier<MainNotification> mailNotifier = notifier; // 逆变
        mailNotifier.Notify(new MainNotification());
    }

     

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

 

协变逆变总结

逆变与协变只能放在泛型接口和泛型委托的泛型参数里面,在泛型中out修饰泛型称为协变,协变(covariant) 修饰返回值 ,协变的原理是把子类指向父类的关系,拿到泛型中。

在泛型中in 修饰泛型称为逆变, 逆变(contravariant )修饰传入参数,逆变的原理是把父类指向子类的关系,拿到泛型中。

 

内置的协变逆变泛型

序号类型名称
1 接口 IEnumerable<out T>
2 委托 Action<in T>
3 委托 Func<out T>
4 接口 IReadOnlyList<out T>
5 接口 IReadOnlyCollection<out T>

 

6. 泛型的应用

手写ORM框架

ORM 框架,对象关系映射。

  1. 从 老师提供utils 文件夹中将DbHelper拷贝至当前你的项目中

  2. nuget引用:1:Microsoft.Extensions.Configuration,2:System.Data.SqlClient

  3. 封装ORM 框架

    public class DbContext<T> where T : class, new()
    {
         /// <summary>
        /// 添加功能
        /// </summary>
        /// <param name="t">要添加的对象</param>
        public void Add(T t)
        {
            // insert into Student(.属性1,属性2,属性3..) values(.'属性值1','属性值2','属性值3'..)
            StringBuilder sql = new StringBuilder($"insert into {typeof(T).Name}(");
            // 跳过第一个属性
            var propertyInfos = typeof(T).GetProperties().Skip(1);
            var propNames = propertyInfos.Select(p => p.Name).ToList();
            sql.Append(string.Join(",", propNames));
            sql.Append(") values('");
    
            List<string> values = new List<string>();
            foreach (var propertyInfo in propertyInfos)
            {
                // 获取属性值
                values.Add(propertyInfo.GetValue(t).ToString());
            }
            sql.Append(string.Join("','", values));
            sql.Append("')");
            DbHelper.ExecuteNonQuery(sql.ToString());
        }
    
        public List<T> GetList()
        {
            return DbHelper.GetList<T>($"select * from {typeof(T).Name}");
        }
    
    
        public T GetModel(dynamic id)
        {
            var pk = GetPrimaryKey().Name; //获取主键的名称
            //获取一条记录
            return DbHelper.GetList<T>(
                $"select * from {typeof(T).Name} where {pk}=@id",
                new SqlParameter(pk, id)).First();
        }
    
        public int Update(T model)
        {
            var tp = typeof(T);
            var pk = GetPrimaryKey(); //获取主键
            var props = tp.GetProperties().ToList();
            //获取所有的属性名称(除主键)
            var propNames = props.Where(p => !p.Name.Equals(pk)).Select(p => p.Name).ToList();
    
    
            //update 表 set 字段1=@字段1,字段2=@字段2, where 主键名=主键值
            string sql = $"update {tp.Name} set ";
            foreach (var propName in propNames)
            {
                sql += $"{propName}=@{propName},";
            }
    
            sql = sql.Remove(sql.Length - 1);
    
            sql += $" where {pk.Name}=@{pk.Name}";
    
            List<SqlParameter> list = new();
            foreach (var prop in props)
            {
                SqlParameter parameter = new SqlParameter(prop.Name, prop.GetValue(model));
                list.Add(parameter);
            }
    
            return DbHelper.ExecuteNonQuery(sql, list.ToArray());
        }
    
        public int Delete(dynamic id)
        {
            //delete from 表名 where 主键名=@主键值
    
            var pk = GetPrimaryKey().Name;
            return DbHelper.ExecuteNonQuery($"delete from {typeof(T).Name} where {pk}=@{pk}", new SqlParameter(pk,id));
        }
    
    
        /// <summary>
        /// 获取主键
        /// </summary>
        /// <returns></returns>
        public PropertyInfo GetPrimaryKey()
        {
            var props = typeof(T).GetProperties();
            foreach (var propertyInfo in props)
            {
                //获取特性
                var attrs = propertyInfo.GetCustomAttributes(typeof(KeyAttribute), false);
                if (attrs.Length > 0)
                {
                    return propertyInfo;
                }
            }
    
            return props[0]; // 如果没有Key 特性,就让第一个属性当作主键
        }
    }

     

     

DataTable 转 List

DataTable 转换成List

private static List<T> ToList<T>(DataTable dt) where T : class, new()
{
    Type t = typeof(T);
    PropertyInfo[] propertys = t.GetProperties();
    List<T> lst = new List<T>();
    string typeName = string.Empty;

    foreach (DataRow dr in dt.Rows)
    {
        T entity = new T();
        foreach (PropertyInfo pi in propertys)
        {
            typeName = pi.Name;
            if (dt.Columns.Contains(typeName))
            {
                if (!pi.CanWrite) continue;
                object value = dr[typeName];
                if (value == DBNull.Value) continue;
                if (pi.PropertyType == typeof(string))
                {
                    pi.SetValue(entity, value.ToString(), null);
                }
                else if (pi.PropertyType == typeof(int) || 
                         pi.PropertyType == typeof(int?))
                {
                    pi.SetValue(entity, int.Parse(value.ToString()), null);
                }
                else if (pi.PropertyType == typeof(DateTime?) || 
                         pi.PropertyType == typeof(DateTime))
                {
                    pi.SetValue(entity, DateTime.Parse(value.ToString()), null);
                }
                else if (pi.PropertyType == typeof(float))
                {
                    pi.SetValue(entity, float.Parse(value.ToString()), null);
                }
                else if (pi.PropertyType == typeof(double))
                {
                    pi.SetValue(entity, double.Parse(value.ToString()), null);
                }
                else
                {
                    pi.SetValue(entity, value, null);
                }
            }
        }

        lst.Add(entity);
    }

    return lst;
}

配套视频链接:

C# 高级编程,.Net6 系列 开发第三阶段,学完拿捏你的面试官,.net6 进阶学习(已完结)_哔哩哔哩_bilibili

 

 

 

标签:接口,第四,public,泛型,new,where,class,单元
From: https://www.cnblogs.com/xuyubing/p/17476723.html

相关文章

  • Java的泛型
    泛型是我们需要的程序设计手段。使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。至少在表面上看来,泛型很像C++中的模板。与Java—样,在C++中,模板也是最先被添加到语言中支持强类型集合的。但是,多年之后人......
  • DataGridViewCheckBoxCell单元格点击后立即更改选中状态
     PrivateSubDataGridView1_CellMouseClick(senderAsObject,eAsDataGridViewCellMouseEventArgs)HandlesDataGridView1.CellMouseClickIfDataGridView1(e.ColumnIndex,e.RowIndex).GetType=GetType(WinForms.DataGridViewCheckBoxCell)Then......
  • 第三部:《天涯赤子心》 第四部:《春天后母心》
    《天涯赤子心》《天涯赤子心》是一部励志亲情剧。主要讲述两个孩子漂洋过海、历尽千辛万苦,到内地寻找亲生父亲的过程中发生的一系列故事。剧中主要围绕小君小杰两个孩子来演,他们寻找父亲途中需要很多误会与陷害,但依旧坚强勇敢面对,最后找到了父亲,一家团聚。 第四部:《春天后......
  • 第四次工业革命
     第四次工业革命,即工业4.0,最早出现在2013年的汉诺威工业博览会,是以物联网、大数据、机器人及人工智能等技术为主导的,继蒸汽技术、电力技术、计算机及信息技术之后的又一次革命。借着全球化的趋势,这次革命正在以前所未有的姿态席卷全球。在这次工业革命中,中国已经成为一股不可忽视......
  • 【React工作记录一百零一】再次接触老朋友react+ant design table合并单元格
    前言大家好我是歌谣今天继续开发一个需求就是我们的大屏需求今天让我们一步步解决所遇到的问题第一个功能如何渲染参照官网案例constdataSource=[{key:'1',name:'胡彦斌',age:32,address:'西湖区湖底公园1号',},{key:'2',nam......
  • 【技术积累】Java中的泛型【一】
    泛型是什么Java中的泛型是一种能够让用户在编写代码时避免使用明确的类型而进行类型参数化的机制。Java中的泛型可以让编程者在代码编写时不必关心具体类型,只用关心类型之间的关系和相互转换,从而在编写代码的过程中实现类型的复用。这使得代码更加简洁、可读性更高,并且可以提高代......
  • 9.17 泛型方法
    实际开发中非常常见没有定义泛型类,可以直接定义使用泛型方法;泛型方法不一定就出现在泛型类之中.publicclassHelloWorld{publicstaticvoidmain(Stringargs[]){Integernum[]=fun(1,2,3);//传入了整数,for(inttmp:num){System......
  • 9.16 泛型接口
    对于泛型接口的子类而言,有2种实现方式demo1在子类中继续进行泛型定义interfaceIMessage<T>{publicStringecho(Tt);}classMessageImpl<S>implementsIMessage<S>{publicStringecho(St){return"[echo]"+t;}}publicclassHello......
  • 2 视觉设计 美化单元格
    如何为你的数据选择格式使用字体来标明什么是最重要的单元格样式使得重复的要素保特一致的格式选择要使用的单元格样式后,继续使用主题来改变电子表格外观......
  • 玩转Google开源C++单元测试框架Google Test系列(gtest)(总)
    前段时间学习和了解了下Google的开源C++单元测试框架GoogleTest,简称gtest,非常的不错。我们原来使用的是自己实现的一套单元测试框架,在使用过程中,发现越来越多使用不便之处,而这样不便之处,gtest恰恰很好的解决了。其实gtest本身的实现并不复杂,我们完全可以模仿gtest,不断的完善我们......