首页 > 编程语言 >学习笔记:C#高级进阶语法——泛型

学习笔记:C#高级进阶语法——泛型

时间:2025-01-02 13:31:18浏览次数:1  
标签:Console 进阶 C# static WriteLine 泛型 public Name

一、Generic(泛型)

1、泛型的定义:通用的类型就是泛型

//在一个方法,传入的参数不确定的时候,我们可能要重写多次这个方法
public void Show(string t)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
public void Show(int t)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
//这种情况也可以将参数定义为object类型来解决这个问题
public void Show(object t)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}
但是object有两个缺陷:
1、类型安全问题
2、性能非常差
通过下面的类子来证明这个问题
using System.Diagnostics;

namespace MyGeneric
{
    public class Monitor
    {
        public static void Show()
        {
            int iValue = 12345;
            long commonSecond = 0;
            long objectSecond = 0;
            long genericSecond = 0;

            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < 100_000_000; i++)
                {
                    ShowInt(iValue);
                }
                sw.Stop();
                commonSecond = sw.ElapsedMilliseconds;
            }
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < 100_000_000; i++)
                {
                    ShowObject(iValue);
                }
                sw.Stop();
                objectSecond = sw.ElapsedMilliseconds;
            }

            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < 100_000_000; i++)
                {
                    ShowGeneric(iValue);
                }
                sw.Stop();
                genericSecond = sw.ElapsedMilliseconds;
            }

            Console.WriteLine($"commonSecond={commonSecond},objectSecond={objectSecond},genericSecond={genericSecond}");
        }
        public static void ShowInt(int iValue) { }
        public static void ShowObject(object oValue){}
        public static void ShowGeneric<T>(T t) { }
    }
}
//所以我们需要泛型来解决这个问题,泛型的性能和固定类型的性能没有什么差距
    public void Show<T>(T t)
{
    Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={t.GetType().Name},type={t}");
}

2、泛型的声明---设计思想

方法在声明的时候,带有尖括号<>,尖括号里有一个占位符T;

T是什么?什么类型? 称之为类型参数

延迟声明:声明时不确定类型,调用时确定类型

3、泛型的特点和原理---在底层如何支持

1、泛型在编译后,变成一个带有 `符号[位数]

2、泛型类在继承时,子类继承的父类是引用类型

4、泛型的多种应用

1、泛型方法----方法名<T>
2、泛型类---类名<T>,特点:为每一个不同类型生成一个唯一的副本
3、泛型接口--接口名<T>
4、泛型委托--Action<int>,Func<int>系统自带的两个泛型委托
5、泛型约束--

5、泛型缓存

using System;
using System.Collections.Generic;
using System.Threading;

namespace MyGeneric.Extension
{
    //能理解80%以上 刷个1
    //能理解60%以上 刷个2

    /// <summary>
    /// 泛型缓存~
    /// </summary>
    public class GenericCacheTest
    {
        public static void Show()
        {

            //普通缓存
            {
                for (int i = 0; i < 5; i++)
                {
                    Console.WriteLine(DictionaryCache.GetCache<int>()); //GenericCacheInt
                    Thread.Sleep(10);
                    Console.WriteLine(DictionaryCache.GetCache<long>());// GenericCachelong
                    Thread.Sleep(10);
                    Console.WriteLine(DictionaryCache.GetCache<DateTime>());
                    Thread.Sleep(10);
                    Console.WriteLine(DictionaryCache.GetCache<string>());
                    Thread.Sleep(10);
                    Console.WriteLine(DictionaryCache.GetCache<GenericCacheTest>());
                    Thread.Sleep(10);
                }
            }

            //泛型缓存--可以根据不同的类型生成一个新的类的副本;生合二老无数的副本;
            {
                for (int i = 0; i < 5; i++)
                {
                    Console.WriteLine(GenericCache<int>.GetCache()); //GenericCacheInt
                    Thread.Sleep(10);
                    Console.WriteLine(GenericCache<long>.GetCache());// GenericCachelong
                    Thread.Sleep(10);
                    Console.WriteLine(GenericCache<DateTime>.GetCache());
                    Thread.Sleep(10);
                    Console.WriteLine(GenericCache<string>.GetCache());
                    Thread.Sleep(10);
                    Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
                    Thread.Sleep(10);
                }
            }
        }
    }

    /// <summary>
    /// 字典缓存:静态属性常驻内存
    /// </summary>
    public class DictionaryCache
    { 
        /// <summary>
        /// 静态字典---常驻内存~ 只要程序进程不停止,就会一直保存数据在内存;
        /// </summary>
        private static Dictionary<Type, string> _TypeTimeDictionary = null;

        //同一个类的静态构造函数在整个进程中,执行且只执行一次;
        static DictionaryCache()
        {
            Console.WriteLine("This is DictionaryCache 静态构造函数");
            _TypeTimeDictionary = new Dictionary<Type, string>();
        }

        public static string GetCache<T>()
        {
            Type type = typeof(T); //获取类的type 类型
            if (!_TypeTimeDictionary.ContainsKey(type))
            {
                //写死的,生成数据;  使用当前这个type 作为key 保存到字典中去
                _TypeTimeDictionary[type] = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
            }
            return _TypeTimeDictionary[type];
        }
    }


    /// <summary>
    ///泛型缓存:
    ///本质就是泛型类---泛型缓存的应用--手写ORM----有应用~~
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericCache<T>
    {
        /// <summary>
        ///同一个类的静态构造函数在整个进程中,执行且只执行一次;
        /// </summary>
        static GenericCache()
        {
            Console.WriteLine("This is GenericCache 静态构造函数");
            //_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff")); 
            _TypeTime = $"{typeof(T).FullName}_{DateTime.Now.ToString("yyyyMMddHHmmss.fff")}";
        }

        /// <summary>
        /// 只缓存一个字符串
        /// </summary>
        private static string _TypeTime = "";

        public static string GetCache()
        {
            return _TypeTime;
        }

    }
}

6、泛型约束

1、C#中引用类型-->保存在进程堆,值类型-->保存在线程栈,从值类型转换为引用类型叫装箱,从引用类型转换为值类型叫拆箱

2、类型安全:允许传入,但是在方法内部又写了类型强转等,在执行的时候会报错。

public static void ShowObject(object oParameter)
{
//Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={oParameter.GetType().Name},type={oParameter}"); 
//以上业务其实太简单了点;
//如果想要用People呢?

//C#是强类型语言,编译时决定类型;
//编译器识别的 oParameter 当成 object来识别的;而object 是没有Id和Name
//访问不了
//Console.WriteLine($"People.Id={oParameter.Id}");
//Console.WriteLine($"People.Name={oParameter.Name}"); //访问不了

//类型转换
People people = (People)oParameter; //类型强转~

Console.WriteLine($"People.Id={people.Id}");
Console.WriteLine($"People.Name={people.Name}"); //访问不了
}

////类型不安全:
////object 类型作为参数,所有类型都可以传入;
////在方法的内部业务处理,可能会因为一些不适合的类型传入,导致一些问题;
////特点: 允许你传入
////       可能会报错~~  
GenericConstraint.ShowObject(people);
GenericConstraint.ShowObject(dtValue);

////最好的情况: 如果方法内部会出错,就应该不让你传入; 如果能传入,就要保证一定没问题;

3、泛型约束:

 //特点:
 //有了约束---传入参数有了局部限制;
 //可以享有一些额外的权利~~

 //有约束才有自由~~  开车~~  红绿灯;

 /// <summary>
 /// 基类约束:
 ///    泛型的类型参数,必须是People或者是People的子类
 ///    否则无法传入;
 ///    就可以把这个类型参数当做People来使用;
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="tParameter"></param>
 public static void ShowGeneric<T>(T tParameter) where T : People
 {
     //Console.WriteLine($"This is {typeof(CommonMethod).Name},parameter={tParameter.GetType().Name},type={tParameter}");
     Console.WriteLine($"People.Id={tParameter.Id}");
     Console.WriteLine($"People.Name={tParameter.Name}"); //访问不了
 }

 ///接口约束
 ///类型参数:必须实现这个接口,才能够传入进来;
 ///就可以把参数当成这个接口使用
 public static void ShowGeneric<T>(T tParameter) where T : IWork
 {
     tParameter.Work();
 }

 ///无参数构造函数约束 
 ///传入的参数,必须包含一个无参数构造函数
 ///就可以直接new T     执行无参数构造函数~
 public static void ShowGeneric<T>(T tParameter) where T : new()
 {
     T tt = new T();
 }

 ///值类型约束
 ///必须要传入结构类型,否则无法传入的 
 public static void ShowGeneric<T>(T tParameter) where T : struct
 {

 }

 ///引用类型约束
 ///必须要传入引用类型,否则无法传入的 
 public static void ShowGeneric<T>(T tParameter) where T : class
 {

 }

///枚举约束
///要求传入的参数必须是一个枚举
public static void ShowGeneric<T>(T tParameter) where T : Enum
{ 
}

public static void ShowGenericNew<T,S,Y,Richard>(T tParameter) where T : struct
{
}

public static void ShowGenericNew2<T, S>(T tParameter) where T : class where S : struct
{
}

7、协变、逆变

1、协变逆变:只针对泛型接口和泛型委托

//任何子类都可以用父类来声明
Animal animal = new Cat();//猫是一个动物

//但是在集合中这样声明会报错
List<Animal> animalList = new List<Cat>();//一堆猫是一堆动物,逻辑上是对的,但是声明的时候会报错
//因为泛型类在传入一个类的类型后,就生成了一个独立的副本,两边生成的副本都是相互独立的,所以左右两边的副本不存在继承关系

//以上的不和谐,引入的协变逆变
//协变,IEnumerable的泛型类型参数用out修饰,类型参数只能作为返回值,不能作为传入参数
//效果:可以让左边使用父类,右边使用子类
IEnumerable<Animal> animals = new List<Cat>();

//逆变, in修饰T,T只能做参数,不能做返回值
//效果:左边使用子类,右边使用父类

标签:Console,进阶,C#,static,WriteLine,泛型,public,Name
From: https://www.cnblogs.com/SevenDouble/p/18647445

相关文章

  • opencv的亚像素角点检测函数cornerSubPix
    在OpenCV中,cornerSubPix函数用于对角点的亚像素精确化。这个函数可以提高角点检测的精度,通常在角点检测之后使用,如在Harris或Shi-Tomasi角点检测之后。函数原型voidcv::cornerSubPix(InputArrayimage,InputOutputArraycorners,SizewinSize,Size......
  • Conda基本使用
    Conda是跨平台的可用于在Windows、macOS和Linux上管理python环境和包的工具;方便在不同项目中创建隔离的python环境,解决python版本或包依赖带来的问题。环境管理Conda版本conda--version更新Conda到最新版本condaupdateconda查看主机存在的cuda环境condaenv......
  • 统信UOS报错qt.qpa.plugin:Could not load the Qt platform plugin "xcb" in “***”
    问题截图: 解决方案:执行命令sudoln-fs/usr/lib/x86_64-linux-gnu/libxcb-util.so.0/usr/lib/x86_64-linux-gnu/libxcb-util.so.1 参考链接:Ubuntu22.04中解决CouldnotloadtheQtplatformplugin“xcb“问题解决方法_couldnotloadtheqtplatformplugin"xcb......
  • CSharp (C#) 中创建Task 的7种方法对比,你用对了吗?
    在C#编程中,Task是用于异步编程的重要工具之一。了解不同创建Task的方式以及它们之间的区别,对提高编程效率和代码质量至关重要。本文将详细探讨几种常见的Task创建方式,并分析它们的使用场景及优缺点。一、使用Task.Run方法1.概述Task.Run是最常见的一种创建Task的方式。它接......
  • 让 Java 再次伟大 - 你不知道的 Java 之集成 Docker 的那些密事(一章节)
    学会这款全新设计的Java脚手架,从此面试不再怕!Docker的底层实现原理Docker是一种容器服务,为了方便理解你可以认为他是一个轻量级的虚拟机。通过Docker创建的容器,在宿主机上共享硬件资源和底层系统与接口,同时又能提供互相隔离的应用程序环境。这是怎么做到的?由于......
  • 2025/1/2 【双指针法】LeetCode27.移除元素 【√】 ❗未完结❗
    27.移除元素-力扣(LeetCode)代码随想录数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。Myanswer:快慢指针法classSolution:defremoveElement(self,nums:List[int],val:int)->int:n=len(nums)j=0forii......
  • opencv中findCirclesGrid在标定时使用,blobDetector 参数怎么定义?
    在OpenCV中,findCirclesGrid是一个用于检测图像中的圆形格点(通常用于相机标定或校正的检测目标)的函数。它可以用于查找在特定模式下排列的圆形图案,如棋盘格或圆形网格。该函数是相机标定和立体视觉的重要工具。函数原型boolcv::findCirclesGrid(InputArrayimage,S......
  • 关于此题E - Maximize XOR(Atcoder ABC 386)搜索技巧的一些总结
    传送门题目要求n个数中选k个数异或起来最大,我们想到字典树中最大异或和这一经典问题,但是很明显字典树只能解决任选两个数的最大异或,而此题是任选k个,那我们走投无路只能考虑爆搜。首先可以很容易写出一个暴力的搜索:voiddfs1(longlongpos,longlongsum,longlongkk){i......
  • [Qt] 万字详解Qt入门~ Qt Creator | 对象树 | 控件布局
    目录1.QtCreator概览2.使用QtCreator新建项目3.认识QtCreator界面4.QtHelloWorld程序1.使用“按钮”实现2.使用“标签”实现3.使用“编辑框”实现5.项目文件解析1.命名空间声明与作用2.classWidget:publicQWidget6.Qt编程注意事项......
  • Pycharm中AI Assistant怎么使用
    当我更新了pycharm到2024.3版本后,发现内置了Jetbrains的aiassistant插件,并且有免费试用的时间,但是并不对中国大陆用户开放,只对提供了openai的地区开放,接下来讲一下我怎么使用的aiassistant,1、安装所需插件 在左上角file-settings中的Plugins的搜索框中查找图中插件并下载......