C#入门级——泛型、泛型类、泛型接口、泛型方法和泛型委托

news2024/12/25 23:37:32

目录

一、泛型(generic)

二、为什么需要泛型类

类型膨胀

成员膨胀

使用object类

三、泛型的定义

定义泛型类

使用泛型类

泛型接口​​​​​​​

两种泛型接口的实现方法

泛型方法

成员膨胀

使用泛型

泛型委托

Action委托——只能引用没有返回值的方法

Func泛型委托——要有返回值

泛型委托和Lambda表达式的配合使用


一、泛型(generic)

泛型类是以实例化过程中提供的类型或类为基础建立的,可对对象进行强类型化

尖括号语法是把类型参数传送给泛型类型的方式

泛型并不限于类,还可以创建泛型接口、泛型方法(可以在非泛型类上定义),甚至泛型委托

泛化和特化是相对的

System.Collections.Generics名称空间的两个类型

类型:List<T>               说明:T类型对象的集合

类型:Dictionary<K,V> 说明:与K类型的键值相关的V类型的项的集合

前文已提及到:

C#集合、定义集合、索引符、List<T>、Dictionary<K,V>、键控集合(键值)和IDictionary_dlwlrma_516的博客-CSDN博客https://blog.csdn.net/dlwlrma_516/article/details/127139139?spm=1001.2014.3001.5501

二、为什么需要泛型类

场景:新百货商店开业,两个类,Chair类,Desk类

不同的货物要用不同的纸箱去装

类型膨胀

现在百货商店新开张,卖Chair和Desk,Chair需要用ChairCarton装,Desk需要用DeskCarton装;但是如果以后百货商店有1000种商品呢?要准备1000种纸箱吗?而且程序不好维护…

创建控制台项目

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Chair chair = new Chair() { Color = "Red" };
            ChairCarton chairCarton = new ChairCarton() { Cargo = chair };
            Console.WriteLine(chairCarton.Cargo.Color);

            Desk desk = new Desk() { Weight = "20kg" };
            DeskCarton deskCarton = new DeskCarton() { Cargo = desk };
            Console.WriteLine(deskCarton.Cargo.Weight);
            Console.ReadKey();
        }
    }

    class Chair
    {
        public string Color { get; set; }
    }

    class Desk
    {
        public string Weight { get; set; }
    }

    class ChairCarton
    {
        public Chair Cargo { get; set; }
    }
    class DeskCarton
    {
        public Desk Cargo { get; set; }
    }
}

成员膨胀

只准备一种纸箱,填写不同的属性,carton1实例只用到了Chair属性,没有用到Desk属性,而carton2属性则相反

以后1000种商品呢?Carton类1000个对应属性,只有1个属性能用,每次增加或减少商品,都要修改Carton类

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Chair chair = new Chair() { Color = "Red" };
            Desk desk = new Desk() { Weight = "20kg" };
            Carton carton1 = new Carton() { ChairCargo = chair };
            Carton carton2 = new Carton() { DeskCargo = desk };
            Console.WriteLine(carton1.ChairCargo.Color);
            Console.WriteLine(carton2.DeskCargo.Weight);
            Console.ReadKey();
        }
    }

    class Chair
    {
        public string Color { get; set; }
    }

    class Desk
    {
        public string Weight { get; set; }
    }

    class Carton
    {
        public Chair ChairCargo { get; set; }
        public Desk DeskCargo { get; set; }
    }
}

使用object类

当把东西往盒子里装时是省事了,但是要把东西从盒子里拿出来时,访问盒子中装的东西就很麻烦

(chair实例赋值给Cargo属性,在Console.WriteLine(carton1.Cargo.时,系统在可选择下拉框中,就已经找不到chair实例下的有关属性Color,需要强制类型转换/as属性操作符)

使用?.的原因:若是个Chair类则打印Color属性,若是Desk类则不会打印Color属性


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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Chair chair = new Chair() { Color = "Red" };
            Desk desk = new Desk() { Weight = "20kg" };
            Carton carton1 = new Carton() { Cargo = chair };
            Carton carton2 = new Carton() { Cargo = desk };
            Console.WriteLine((carton1.Cargo as Chair)?.Color);
            Console.WriteLine((carton2.Cargo as Desk)?.Weight);
            Console.ReadKey();

        }
    }

    class Chair
    {
        public string Color { get; set; }
    }

    class Desk
    {
        public string Weight { get; set; }
    }
    class Carton
    {
        public Object Cargo { get; set; }
    }
}

三、泛型的定义

泛型的定义:泛型类、泛型接口、泛型方法、泛型委托

定义泛型类

<类型参数>     类型参数:标识符,代表泛化的类型

创建泛型类——需在类定义中包含尖括号

class GenericClass<T>
{

}

class Carton<TCargo>

TCargo类型是商品的类型(写的是类名!!)

声明属性——public TCargo Cargo{get;set;}

定义泛型Carton类

class Carton<TCargo>
{
	public TCargo Cargo{get;set;}
}

使用泛型类

使用泛型Carton类

泛型编程实体都不能拿来编程,想要使用泛型实体之前都必须要进行特化

特化

先将泛型Carton类先特化为装Chair的Carton类型:

Carton<Chair>,相当于Chair类型替换了TCargo类型(TCargo-->Chair)

(属性:public TCargo-->public Chair         TCargo类型变成了Chair类型)

在main函数中进行特化:

Carton<Chair> carton1= new Carton<Chair>(){Cargo = Chair};

凡是使用到了类型参数的地方都是强类型的

Cargo属性强类型,属性类型是Chair类型,Chair类型的实例有Color这个属性

Cargo属性强类型,属性类型是Desk类型,Desk类型的实例有Weight这个属性

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Chair chair = new Chair() { Color = "Red" };
            Desk desk = new Desk() { Weight = "20kg" };
            Carton<Chair> carton1 = new Carton<Chair>() { Cargo = chair };
            Carton<Desk> carton2 = new Carton<Desk>() { Cargo = desk };
            Console.WriteLine(carton1.Cargo.Color);
            Console.WriteLine(carton2.Cargo.Weight);
            Console.ReadKey();
        }
    }

    class Chair
    {
        public string Color { get; set; }
    }

    class Desk
    {
        public string Weight { get; set; }
    }

    class Carton<TCargo>
    {
        public TCargo Cargo { get; set; }
    }
}

泛型接口

声明接口,保证对象的唯一——具有ID属性,ID属性的类型不确定,接口改为泛型接口

类型参数:TId——ID属性的类型

接口中不能够加public,因为默认就是public

interface IUnique<TId>
{
    TId ID { get; set; }
}

Student类实现IUnique接口,派生自IUnique

类型参数:TId

如果一个类实现了泛型接口,那么它本身也是泛型的

实现接口,就必须把接口的全部成员都实现

泛型的Student类,TId类型参数只会影响到ID这个属性的类型

实现ID属性:

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student<TId> : IUnique<TId>
    { 
        public TId ID { get; set; }
    }

给Student类添加字符串类型的名字Name

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            
        }
    }

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student<TId> : IUnique<TId>
    { 
        public TId ID { get; set; }
        public string Name { get; set; }
    }
}

main函数中使用Student类

设置Student类的ID是整数:Student<int>

特化Student类后,使用Student类

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student<int> stu = new Student<int>();
            stu.ID = 10000;
            stu.Name = "Tom";
        }
    }

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student<TId> : IUnique<TId>
    { 
        public TId ID { get; set; }
        public string Name { get; set; }
    }
}

随着学校的扩张,ID需要从int改为无符号长整型

ulong作为类型参数传给泛型student类,id的类型就是ulong型

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student<ulong> stu = new Student<ulong>();
            stu.ID = 10000000000000000;
            stu.Name = "Tom";
        }
    }

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student<TId> : IUnique<TId>
    { 
        public TId ID { get; set; }
        public string Name { get; set; }
    }
}

两种泛型接口的实现方法

1、声明Student类时,实现IUnique泛型接口,这时Student类成为泛型类

(前面提及到的情况)

2、实行泛型接口时,实现的是特化后的泛型接口,类不再是泛型类

(下面提及到的情况)

实现IUnique接口时,不再用TId,确定ID的类型一定就是ulong,直接特化了接口,Student类不是泛型类了

当声明一个类时,在实现接口时,就已经把接口特化了,类实现的就是特化后的泛型接口

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            stu.ID = 10000000000000000;
        }
    }

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student : IUnique<ulong>
    { 
        public ulong ID { get; set; }
    }
}

添加Name属性

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            stu.ID = 10000000000000000;
            stu.Name = "Tom";
        }
    }

    interface IUnique<TId>
    {
        TId ID { get; set; }
    }

    class Student : IUnique<ulong>
    { 
        public ulong ID { get; set; }
        public string Name { get; set; }
    }
}

因为IList时泛型接口,而右边的List是泛型类,泛型类实现了泛型接口

带有一个类型参数的IList泛型接口,带有一个类型参数的List泛型类

C#的List就是ArrayList 长度可以改变,动态数组

运行结果:打印出来0-99

泛型接口IList用整型特化下,List泛型类

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IList<int> list = new List<int>();
            for (int i = 0; i < 100; i++)
            { 
                list.Add(i);
            }

            foreach (int item in list)
            { 
                Console.WriteLine(item);
            }
            Console.ReadLine();

        }
    }
}

类型参数为T,实现了IList泛型基接口、ICollection泛型基接口、IEnumerable泛型基接口……

List一定是可以被迭代的,因为实现了IEnumerable泛型基接口

ICollection泛型基接口——集合,可以往集合中增加新元素和删除元素

很多泛型类型带不止一个类型参数,如:IDictionary泛型接口

举例:IDictionary泛型接口

int类型特化TKey类型  string类型特化TValue类型

使用特化后的IDictionary接口类型变量 引用 特化后的Dictionary类型实例

多态:IDictionary接口类型的变量dict可以引用一个Dictionary类型的实例,Dictionary泛型类实现了IDictionary接口,类型参数都是TKey TValue

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            IDictionary<int, string> dict = new Dictionary<int, string>();
            dict[1] = "Jack";
            dict[2] = "Mary";
            Console.WriteLine($"Student #1 is{dict[1]}");
            Console.WriteLine($"Student #2 is{dict[2]}");
            Console.ReadKey();

        }
    }
}

泛型方法

举例:两个整型数组合并一起

不使用泛型方法时,会出现以下情况:

成员膨胀

(方法也是类的成员)

更加容易出问题,两个方法都是静态的,类的静态成员,两个重载方法,除了类型不同以外,其他的逻辑都是相同的,万一修bug只修了其中一个方法,另一个方法忘记了就出问题了

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] a1 = { 1, 2, 3, 4, 5 };
            int[] a2 = { 1, 2, 3, 4, 5, 6 };
            double[] a3 = { 1.1, 2.2, 3.3, 4.4, 5.5 };
            double[] a4 = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 };
            var result = Zip(a1, a2);
            Console.WriteLine(string.Join(",",result));
            Console.ReadKey();
        }

        static int[] Zip(int[] a, int[] b)
        {
            int[] zipped = new int[a.Length + b.Length];
            int ai = 0, bi = 0, zi = 0;
            do
            {
                if (ai < a.Length) zipped[zi++] = a[ai++];
                if (bi < b.Length) zipped[zi++] = b[bi++];
            } while (ai < a.Length || bi < b.Length);

            return zipped;
        }

        static double[] Zip(double[] a, double[] b)
        {
            double[] zipped = new double[a.Length + b.Length];
            int ai = 0, bi = 0, zi = 0;
            do
            {
                if (ai < a.Length) zipped[zi++] = a[ai++];
                if (bi < b.Length) zipped[zi++] = b[bi++];
            } while (ai < a.Length || bi < b.Length);

            return zipped;
        }
    }

}

使用泛型

方法后面加<T>

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int[] a1 = { 1, 2, 3, 4, 5 };
            int[] a2 = { 1, 2, 3, 4, 5, 6 };
            double[] a3 = { 1.1, 2.2, 3.3, 4.4, 5.5 };
            double[] a4 = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 };
            var result = Zip(a1, a2);    //可以不写Zip<int>(a1, a2);
            Console.WriteLine(string.Join(",",result));
            Console.ReadKey();
        }

        static T[] Zip<T>(T[] a, T[] b)
        {
            T[] zipped = new T[a.Length + b.Length];
            int ai = 0, bi = 0, zi = 0;
            do
            {
                if (ai < a.Length) zipped[zi++] = a[ai++];
                if (bi < b.Length) zipped[zi++] = b[bi++];
            } while (ai < a.Length || bi < b.Length);

            return zipped;
        }
    }

}

可以无需显式写<T> <int>或<double> 泛型方法在调用时类型参数自动推断

var result = Zip(a1,a2);   var result = Zip<int>(a1,a2);

泛型委托

Action委托——只能引用没有返回值的方法

静态方法Say/Mul,没有返回值

使用泛型委托分别引用参数类型完全不同的方法

类型参数:告诉Action委托未来要引用的方法的参数类型是什么,有多少个参数

Action委托引用Say方法,Say方法只有一个String类型参数

通过委托去调用一个方法叫间接调用

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Action<string> a1 = Say;
            a1.Invoke("Jack");

            Console.ReadKey();
        }

        static void Say(string str)
        { 
            Console.WriteLine($"Hello,{str}!");
        }
        static void Mul(int x)
        { 
            Console.WriteLine(x * 100);
        }
    }

}

直接像一个方法一样调用,委托本身就是一种可调用的类型

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Action<string> a1 = Say;
            a1("Jack");

            Console.ReadKey();
        }

        static void Say(string str)
        { 
            Console.WriteLine($"Hello,{str}!");
        }
        static void Mul(int x)
        { 
            Console.WriteLine(x * 100);
        }
    }

}

创建泛型委托实例,引用Mul函数,有一个int类型参数

调用a2委托,间接调用Mul方法

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Action<string> a1 = Say;
            a1("Jack");
            Action<int> a2 = Mul;
            a2(1);
            Console.ReadKey();
        }

        static void Say(string str)
        { 
            Console.WriteLine($"Hello,{str}!");
        }
        static void Mul(int x)
        { 
            Console.WriteLine(x * 100);
        }
    }

}

Func泛型委托——要有返回值

Func<类型参数:引用的方法有多少个参数及参数类型,最后一个类型参数:函数返回值类型>

特化Func<double,double,double>泛型委托

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Func<double, double, double> func1 = Add;
            var result = func1(100.1, 200.2);
            Console.WriteLine(result);
            Console.ReadKey();
        }

        static double Add(double a,double b)
        { 
            return a + b;
        }
    }
}

泛型委托和Lambda表达式的配合使用

对于逻辑简单的方法,不去声明,随调用去声明,匿名声明

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Func<double, double, double> func1 = (double a, double b) => { return a + b; };
            var result = func1(100.1, 200.2);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

可以再简化

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

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Func<double, double, double> func1 = (a, b) => { return a + b; };
            var result = func1(100.1, 200.2);
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有效的需求管理,需遵循四大原则。

1、需求管理必须与需求工程活动相整合 需求管理必须与需求工程的其他活动紧密整合&#xff0c;进行需求管理一定不能脱离需求工程&#xff0c;需求工程包括了需求获取、需求分析、需求描述、需求验证、需求管理&#xff0c;因而需求管理必须与前面的几个需求阶段保持密切相关。…

2023/1/9 Vue学习笔记-5-TodoList案例

1 静态组件 App.vue <template><div class"todo-container"><div class"todo-wrap"><UserHeader/><UserList/><UserFooter/></div></div> </template> <script>import UserHeader from &qu…

【计组】FPGA和ASIC--《深入浅出计算机组成原理》(七)

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 目录 一、FPGA &#xff08;一&#xff09;FPGA 的解决方案步骤 1、用存储换功能实现组合逻辑 2、对于需要实现的时序逻辑电路&#xff0c;在 FPGA 里面直接放上 D 触发器&#xff0c;作为寄存…

工业清洗企业资质证书

工业清洗在美国、日本、新加坡、西欧等国发展较早&#xff0c;已经建立起专业化程度很高的化学清洗体系。我国的工业清洗发展很快&#xff0c;目前已经初步形成了新兴的清洗产业网络&#xff0c;清洗技术也已达到国际先进水平&#xff0c;具备了清洗大型设备的能力和经验。 工业…

CANoe-诊断控制台实现同一个目标ECU的物理寻址和功能寻址

接触过UDS诊断的人应该知道,诊断通信有两种方式:物理寻址和功能寻址。那什么是物理寻址和功能寻址呢? 简单点说,物理寻址是单播,功能寻址是多播。具体来说,由于UDS诊断通信的C/S模式(客户端Tester/服务器ECU),物理寻址是Tester发送的诊断请求,只有一个目标ECU回复诊…

MySQL模块

目录 1.在项目中操作数据库的步骤 2.安装与配置 mysql 模块 1.安装模块 2.配置mysql模块 3.测试模块是否正常工作 3.使用 mysql 模块操作 MySQL 数据库 查询数据&#xff1a; 插入数据&#xff1a; 快捷插入数据&#xff1a; 更新数据&#xff1a; 快捷更新数据&am…

node.js(3)--线程和进程、node简介

目录 进程和线程 Node.js 简介 历史 进程和线程 进程 负责为程序的运行提供必备的环境就相当于工厂中的车间&#xff08;专门存放代码的地方&#xff09; 线程 计算机中最小的计算单位&#xff0c;线程负责进程中的程序就相当于工厂中的工人 单线程 JS是单线程 多线程 …

ansible (第三天)

2.6 lineinfile模块 lineinfile模块&#xff0c;确保"某一行文本"存在于指定的文件中&#xff0c;或者确保从文件中删除指定的"文本"&#xff08;即确保指 定的文本不存在于文件中&#xff09;&#xff0c;还可以根据正则表达式&#xff0c;替换"某一…

测牛学堂:软件测试python基础学习之数据类型详解(一)

python数据类型详解 为什么需要数据类型呢&#xff1f; 我们人脑可以轻松的区别不同类型的数据&#xff0c;比如看到1你就知道是数字&#xff0c;但是计算机做不到。 计算机工作的过程就是完成不同的类型的计算&#xff0c;例如做数学运算&#xff0c;做文件存储&#xff0c;逻…

【技术分享】Windows平台低延迟RTMP、RTSP播放器接口设计探讨

背景我们看过了太多介绍RTSP、RTMP播放相关的技术资料&#xff0c;大多接口设计简约&#xff0c;延迟和扩展能力也受到一定的局限&#xff0c;好多开发者希望我们能从接口设计的角度&#xff0c;大概介绍下大牛直播SDK关于RTMP、RTSP播放器开发设计&#xff0c;本文以Windows平…

redis 运维讲解02

一、数据持久化 1、为什么要持久化 redis 重启后&#xff0c;redis 存在内存数据中数据丢失&#xff0c;不管之前是多少G数据&#xff0c;秒丢&#xff0c;而且无法恢复&#xff0c;数据在内存中 [root86-5-master ~]# redis-cli -p 6379 127.0.0.1:6379> MSET k1 v1 k2…

浏览器相关知识

本文主要进行浏览器相关知识的整理总结。 一、浏览器的存储 浏览器的存储包括cookie&#xff0c;session&#xff0c;LocalStorage&#xff0c;sessionStorage&#xff0c;indexedDB。 作用cookiesessionsessionStorageLocalStorageindexedDB储存时间设置或不设置默认30分钟仅…

就只有这么简单?全自动加药装置远程维护解决方案

一、行业背景说起工业生产&#xff0c;给人们的普遍印象都是浓烟&#xff0c;废水&#xff0c;环境污染。尤其是石油、化工、发电厂等一些具有大型设备的地方&#xff0c;确实常常都会有浓烟和污水产出&#xff0c;让人看了恨不得离得越远越好&#xff01;但是随着现代科技的发…

java和vue开发的电子书系统自动检测敏感词小说网站

简介 电子书系统&#xff0c;注册用户上传txt&#xff0c;系统自动检测敏感词汇并且自动生成章节。管理员审核电子书&#xff0c;管理电子书分类和用户&#xff0c;评论等。注册用户可以搜索浏览电子书&#xff0c;在线阅读和下载电子书。 演示视频&#xff1a;https://www.b…

Java设计模式-单例模式Singleton

介绍 所谓类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法(静态方法)。 比如 Hibernate 的 SessionFactory&#xff0c;它充当数据存储源的代理&#xff0…

系分 - 案例分析 - 系统规划

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录系分 - 案例分析 - 系统规划典型例题 1题目描述参考答案典型例题 2题目描述参考答案典型例题 3题目描述参考答案系分 - 案例分析 - 系统规划 典型例题 1 题目描述 某软件开发企业受对外贸易公司委托开发…

STM32F429连接USB飞行摇杆

本文介绍如何使用stm32连接usb接口的飞行摇杆。开发环境硬件: STM32F429IGT6开发板&#xff0c;USB接口的飞行摇杆。软件&#xff1a;STM32CubeIDE 1.11仿真器&#xff1a;stlink参考书&#xff1a;《圈圈教你玩USB》USB设备描述符一个USB设备只有一个设备描述符。USB主机通过不…

springboot3.0+GraalVM搭建云原生环境

1.首先下载安装GraalVM 选择java17.windows(amd64),最好选择VPN下载 下载完成以后解压&#xff0c;如图 然后配置环境变量 配置GRAALVM_HOME&#xff0c;如图 然后在PATH里面添加 %GRAALVM_HOME%\bin 配置完成以后&#xff0c;在cmd里面执行java -version,可以看到jdk已经是…

Apache Shiro教程(2)

Shiro实战教程 1、权限的管理 1.1、什么是权限管理 1、基本上涉及到用户参与的系统都需要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资…

【 uniapp - 黑马优购 | 加入购物车】如何配置 vuex、加入购物车功能并持久化

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc…