·来源于唐老狮的视频教学,仅作记录和感悟记录,方便日后复习或者查找


一.C#版本与Unity的关系

1.各Unity版本支持的C#版本

更多信息可以在Untiy官网说明查看
https://docs.unity3d.com/2020.3/Documentation/Manual/CSharpCompiler.html(这个好像要科学上网)

2.为什么不同版本Unity支持的C#版本不同

是因为不同版本的Unity使用的 C#编译器 脚本运行时版本不同

3.不同版本C#的意义

主要就是可以使用新功能,来节约代码量,让代码更简单直观简洁

4.Unity中调整.Net API 兼容级别

可以去工程中选择对应的兼容级别

目前新版本(这里是2022.3.62f1c1)分为了.Net Standard 2.1 和 .Net Framework

.Net Framework(特殊需求时):

  • 具备较为完整的.Net API,甚至包含了一些无法跨平台的API
  • 如果你的应用主要针对Windows平台,并且会使用到.Net Standard 2.0中没有的功能时会选择使用它
  • 包体更大

.Net Standard 2.1(常规情况下):

  •  是一个.Net标准API集合,相对.Net Framework包含更少的内容,可以减小最终可执行文件大小
  •  它具有更好的跨平台支持
  • .Net Standard 2.1 配置文件大小是.Net Framework配置文件的一半

通常情况下,为了跨平台和更小的包体,我们都选择使用默认的.Net Standard 2.1


二.C#1~4

1.Unity最低支持的C#版本

2.C#1~4的功能和语法

这里只要提及一些Unity开发中常用的功能与特性

3.补充内容--命名与可选参数

可以让我们在调用函数的时候,指定传入的数据是传入到哪一个参数上的。例如如下:

public void Test(int i, float f, bool b)
{}public void Test2(int i , bool b = true, string s = "123")
{}//有了命名参数,我们将不用匹配参数在所调用方法中的顺序
//每个参数可以按照参数名字进行指定
Test(1, 1.2f, true);
Test(f: 3.3f, i: 5, b: false);
Test(b: false, f: 3.4f, i: 3);//命名参数可以配合可选参数使用,让我们做到跳过其中的默认参数直接赋值后面的默认参数
Test2(1, true, "234");
Test2(1, s: "234");

这样的好处是:

①可以跳过一些默认参数去赋值后面的参数

②通过好处一可以让我们少写一些代码与重载函数

4.补充内容--动态类型

关键词:dynamic

作用:能够接收任意类型的不为NULL的变量,类似于Object,但是不是把他装箱为Object,而是直接动态地解析成该变量的类型,我们可以在代码后面直接使用该变量去赋值或者调用里面的成员变量与方法(不过不会有提示,需要我们自己保证拼写没有出错)。更详细的解释如下:

注意事项:

  • 使用dynamic功能 需要将Unity的.Net API兼容级别切换为.Net Framework
  • IL2CPP 不支持 C# dynamic 关键字。它需要 JIT 编译,而 IL2CPP 无法实现
  • 动态类型是无法自动补全方法的,我们在书写时一定要保证方法的拼写正确性
  • 该功能我们只做了解,不建议大家使用

使用例子:

using UnityEngine;public class TestClass
{public string name = "Test Me";public void PrintName(){Debug.Log(name);}
}public class Lesson3 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){//不确定要赋值的类型的时候使用dynamic i = 1;print(i);//可以接受一个类类型dynamic t = new TestClass();//去调用其中的方法t.PrintName(); print(t.GetType());    }
}

好处:

  • 可以让我不用自己去关心和处理类型转化相关的事项,节约代码。
  • 不确定对象类型,但是确定对象成员时,可以使用动态类型通过反射处理某些功能时,也可以考虑使用动态类型来替换它

三.C#5

1.C#5的新功能和新语法

①调用方信息特性(C#进阶套课——特性)

using System.Runtime.CompilerServices;public static void Log(string message,[CallerMemberName] string caller = null,[CallerFilePath] string file = null,[CallerLineNumber] int line = 0)
{Console.WriteLine($"【{message}】| 调用者:{caller} | 文件:{file} | 行号:{line}");
}// 调用示例
public class Demo
{public void Test(){Log("发生错误"); // 编译器自动注入调用信息}
}

②异步方法async和await

2.回顾内容--线程

Unity中线程使用的注意事项:

  • Unity支持多线程
  • Unity中开启的多线程不能使用主线程中的对象(不能使用Unity的API)
  • Unity中开启多线程后一定记住关闭(一般在一个对象中创建线程后,在OnDestory中关闭线程)

线程的使用例子:

//创建一个新线程对象
t = new Thread(()=> {//线程中进行一个死循环while (true){print("123");//每次运行到这里的时候让线程休眠1秒Thread.Sleep(1000);}
});
//启用线程
t.Start();//下面是主线程的代码
print("主线程执行");

3.补充内容--线程池

3.1.原理与特性

命名空间:System.Threading

类名:ThreadPool(线程池)

目的:

  • 在多线程的应用程序开发中,频繁的创建删除线程会带来性能消耗,产生内存垃圾
  • 为了避免这种开销C#推出了 线程池ThreadPool类

大致原理:

  • ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务
  • 任务执行完毕后线程不会销毁,而是被线程池回收以供后续任务使用
  • 当线程池中所有的线程都在忙碌时,又有新任务要处理时,线程池才会新建一个线程来处理该任务,
  • 如果线程数量达到设置的最大值任务会排队,等待其他任务释放线程后再执行

优点:

  • 线程池能减少线程的创建,节省开销,可以减少GC垃圾回收的触发

缺点:

  • 不能控制线程池中线程的执行顺序,也不能获取线程池内线程取消/异常/完成的通知

3.2.内部的方法使用

//1.获取可用的工作线程数和I/O线程数
int num1;
int num2;
ThreadPool.GetAvailableThreads(out num1, out num2);
print(num1);
print(num2);//2.获取线程池中工作线程的最大数目和I/O线程的最大数目ThreadPool.GetMaxThreads(out num1, out num2);print(num1);print(num2);

//3.设置线程池中可以同时处于活动状态的 工作线程的最大数目和I/O线程的最大数目
//  大于次数的请求将保持排队状态,知直到线程池线程变为可用
//  更改成功返回true,失败返回false
if(ThreadPool.SetMaxThreads(20, 20))
{print("更改成功");
}

//5.设置 工作线程的最小数目和I/O线程的最小数目
if(ThreadPool.SetMinThreads(5, 5))
{print("设置成功");
}//4.获取线程池中工作线程的最小数目和I/O线程的最小数目
ThreadPool.GetMinThreads(out num1, out num2);
print(num1);
print(num2);

最小线程数是:在初始的时候一直保持至少有这么多个线程是在运行的(即使它没有执行什么任务,但是一旦有任务可以马上用它来执行)

最大线程数是:如果当前线程数已经达到了最大线程数,当有新任务的时候,就会先排队等待线程资源被释放出来

//6.将方法排入队列以便执行,当线程池中线程变得可用时执行//ThreadPool.QueueUserWorkItem((obj) =>
//{
//    print(obj);
//    print("开启了一个线程");
//}, "123452435345");for (int i = 0; i < 10; i++)
{//第一个参数传入一个回调函数,第二个参数传入一个要在回调中使用的参数值(传给obj了)ThreadPool.QueueUserWorkItem((obj) =>{print("第" + obj + "个任务");}, i);
}print("主线程执行");

4.补充内容----Task任务类

4.1.认识Task

命名空间:System.Threading.Tasks
类名:Task

说明:Task就是任务的意思,它继承了线程池的优点的同时改进了线程池的缺点,即创建的任务能够被精确地控制。它是基于线程池的优点的一个封装类,可以帮助我们更加高效地完成多线程相关的开发。

4.2.创建Task的方式

4.2.1.创建无返回值的Task的三种方式
        //1.通过new一个Task对象传入委托函数并启动Task t1 = new Task(() => {int i = 0;while (isRuning) {print("方式一:" + i);++i;Thread.Sleep(1000);}});t1.Start();//2.通过Task中的Run静态方法传入委托函数Task t2 = Task.Run(() => {int i = 0;while (isRuning) {print("方式二:" + i);++i;Thread.Sleep(1000);}});//3.通过Task.Factory中的StartNew静态方法传入委托函数Task t3 = Task.Factory.StartNew(() => {int i = 0;while (isRuning) {print("方式三:" + i);++i;Thread.Sleep(1000);}});

①new一个Task对象,传入委托函数

②使用Task.Run静态方法传入委托函数

③使用Task.Factory.StartNew()传入委托函数

4.2.2.创建有返回值的Task的三种方式
        //1.通过new一个Task对象闯入委托函数并启动t1 = new Task<int>(() => {int i = 0;while (isRuning) {print("方式一:" + i);++i;Thread.Sleep(1000);}return 1;});t1.Start();//2.通过Task中的Run静态方法传入委托函数t2 = Task.Run<string>(() => {int i = 0;while (isRuning) {print("方式二:" + i);++i;Thread.Sleep(1000);}return "1231";});//3.通过Task.Factory中的StartNew静态方法传入委托函数t3 = Task.Factory.StartNew<float>(() => {int i = 0;while (isRuning) {print("方式三:" + i);++i;Thread.Sleep(1000);}return 4.5f;});

①方式和无返回值的基本方法差不多:new,Task.Run(),Task.Factory.RunNew()

②不同的在于这些方法后面要多加一个用于告诉返回值是多少的泛型,并且传入的委托函数中要有相对应的返回值

//获取返回值
//注意:
//Resut获取结果时会阻塞线程
//即如果task没有执行完成
//会等待task执行完成获取到Result
//然后再执行后边的代码,也就是说 执行到这句代码时 由于我们的Task中是死循环 
//所以主线程就会被卡死
print(t1.Result);
print(t2.Result);
print(t3.Result);print("主线程执行");

①通过task变量的值.Result获取里面返回的值

②要注意一旦使用这个方法的时候,如果该线程中还没有计算完成返回值的话就会一直阻塞主线程的执行

4.3.同步执行Task

默认情况下各个线程和主线程之间肯定是异步执行的

同步执行Task的意思大概可以粗暴地理解成把Task中的任务再放回主线程中执行。

//如果你希望Task能够同步执行
//只需要调用Task对象中的RunSynchronously方法
//注意:需要使用 new Task对象的方式,因为Run和StartNew在创建时就会启动Task t = new Task(() => {Thread.Sleep(1000);print("哈哈哈");
});
//t.Start();
t.RunSynchronously();
print("主线程执行");
//不Start 而是 RunSynchronously

①在开始线程的时候用的不是Start()而是RunSynchronously()

②注意这个只能用在用new一个Task的时候使用,因为另外两种方法会直接默认Start()线程

4.4.Task中线程阻塞的方式(任务阻塞)

即让一个线程执行完了之后才能够继续执行后面的内容

当我们需要在某几个任务完成之后再去执行其他任务的时候可以使用这个方法

//1.Wait方法:等待任务执行完毕,再执行后面的内容
Task t1 = Task.Run(() =>
{for (int i = 0; i < 5; i++){print("t1:" + i);}
});Task t2 = Task.Run(() =>
{for (int i = 0; i < 20; i++){print("t2:" + i);}
});
t2.Wait();//2.WaitAny静态方法:传入任务中任意一个任务结束就继续执行
//Task.WaitAny(t1, t2);//3.WaitAll静态方法:任务列表中所有任务执行结束就继续执行
//Task.WaitAll(t1, t2);//print("主线程执行");

①可以直接让t1.wait()

②可以用Task中的静态方法.WaitAny(),传入可变参数个任务,等待其中任意一个任务完成之后就继续执行

③可以用Task中的静态方法.WaitAll(),传入可变参数个任务,等待所有这些任务完成之后就继续执行

4.5.Task中完成后续其他Task(任务延续)

即让Task按一定顺序接续执行

当我们需要让某几个任务之间按一定顺序完成的时候就要使用这个方法

//1.WhenAll静态方法 + ContinueWith方法:传入任务完毕后再执行某任务
Task.WhenAll(t1, t2).ContinueWith((t) => {print("一个新的任务开始了");int i = 0;while (isRuning) {print(i);++i;Thread.Sleep(1000);}
});Task.Factory.ContinueWhenAll(new Task[] { t1, t2 }, (t) => {print("一个新的任务开始了");int i = 0;while (isRuning) {print(i);++i;Thread.Sleep(1000);}
});//2.WhenAny静态方法 + ContinueWith方法:传入任务只要有一个执行完毕后再执行某任务
Task.WhenAny(t1, t2).ContinueWith((t) => {print("一个新的任务开始了");int i = 0;while (isRuning) {print(i);++i;Thread.Sleep(1000);}
});Task.Factory.ContinueWhenAny(new Task[] { t1, t2 }, (t) => {print("一个新的任务开始了");int i = 0;while (isRuning) {print(i);++i;Thread.Sleep(1000);}
});

①可以通过Task的静态类.WhenAll和.WhenAny中的.ContinueWith方法,或者Task.Factory中的静态方法.ContinueWhenAll()和.ContinueWhenAny()来指定所有任务完成后或者其中任意一个任务完成之后再去执行的任务

②委托函数中传入的参数t是一个Task类型的,它是里面包含了前置的那些任务的执行状态之类的变量。比如可以按如下方式使用:

.ContinueWith(t => {if (t.IsFaulted) {Debug.LogError($"任务失败: {t.Exception}");return;}// 安全执行后续逻辑...
});

4.6.取消Task执行

方法一:通过加入bool标识 控制线程内死循环的结束

方法二:通过CancellationTokenSource取消标识源类 来控制
CancellationTokenSource对象可以达到延迟取消、取消回调等功能

 //声明一个取消标识源类c = new CancellationTokenSource();//延迟取消c.CancelAfter(5000);//取消回调c.Token.Register(() =>{print("任务取消了");});Task.Run(() =>{int i = 0;//用里面的是否被撤销的属性来控制循环while (!c.IsCancellationRequested){print("计时:" + i);++i;Thread.Sleep(1000);}});//延迟取消// Update is called once per frame
void Update()
{if(Input.GetKeyDown(KeyCode.Space)){//调用取消标识源中的取消方法来取消任务c.Cancel();}
}

①可以在一开始的时候设定一个控制取消的延迟时间

②可以在取消的时候触发一个取消回调事件

③可以用是否撤销的属性来控制线程是否被撤销

5.异步方法

5.1.什么是同步和异步

同步和异步主要用于修饰方法
同步方法:
当一个方法被调用时,调用者需要等待该方法执行完毕后返回才能继续执行
异步方法:
当一个方法被调用时立即返回,并获取一个线程执行该方法内部的逻辑,调用者不用等待该方法执行完毕

5.2.什么时候需要异步编程

当我们调用的方法计算量比较大且耗时的时候,我们不希望花太多时间在这上面阻塞主线程。这个时候据需要使用异步方法了。

比如:

  • 1.复杂逻辑计算时
  • 2.网络下载、网络通讯
  • 3.资源加载时

等等

5.3.异步方法async和await

async和await一般需要配合Task进行使用

async:负责修饰方法,告诉编译器这个是异步方法

await:负责在异步方法内部返回一个线程中的执行逻辑,此时会回到函数外部的主线程中,当该线程中的这部分执行逻辑执行结束了之后才会继续执行函数后面的逻辑

  • 在一个async异步函数中可以有多个await等待关键字

使用await等待异步内容执行完毕(一般和Task配合使用)
遇到await关键字时发生的事情:

  • 1.异步方法将被挂起
  • 2.将控制权返回给调用者
  • 3.当await修饰内容异步执行结束后,继续通过调用者线程执行后面内容
//异步方法执行流程示意
public async void TestAsync()
{//1print("进入异步方法");//2.这步会在一个线程中执行,此时程序返回到函数外部的主线程中继续执行await Task.Run(() =>{Thread.Sleep(5000);});//3.当第二步中的线程执行完毕了之后,就会继续执行这之后的逻辑print("异步方法后面的逻辑");
}

使用async修饰异步方法的注意事项:

  • 1.在异步方法中使用await关键字(不使用编译器会给出警告但不报错),否则异步方法会以同步方式执行
  • 2.异步方法名称建议以Async结尾
  • 3.异步方法的返回值只能是void、Task、Task<>
  • 4.异步方法中不能声明使用ref或out关键字修饰的变量

5.4.使用例子

5.4.1.复杂逻辑计算

利用Task新开线程进行计算 计算完毕后再使用 比如复杂的寻路算法
CalcPathAsync(this.gameObject, Vector3.zero);

 public async void CalcPathAsync(GameObject obj, Vector3 endPos){print("开始处理寻路逻辑");int value = 10;await Task.Run(() =>{//处理复杂逻辑计算 我这是通过 休眠来模拟 计算的复杂性Thread.Sleep(1000);value = 50;//是多线程 意味着我们不能在 多线程里 去访问 Unity主线程场景中的对象//这样写会报错//print(obj.transform.position);});print("寻路计算完毕 处理逻辑" + value);obj.transform.position = Vector3.zero;}

5.4.2.计时器

顾名思义,就是按时间计时了

Timer();
print("主线程逻辑执行");

 public async void Timer(){UnityWebRequest q = UnityWebRequest.Get("");source = new CancellationTokenSource();int i = 0;while (!source.IsCancellationRequested){print(i);await Task.Delay(1000);++i;}}// Update is called once per frame
void Update()
{if (Input.GetKeyDown(KeyCode.Space))source.Cancel();
}

5.4.3.资源加载

Addressables的资源异步加载是可以使用async和await的

Resources.LoadAsync()这些早期的异步加载资源的方式在await这里用不了,只有使用协同程序进行使用。除非搭配一些第三方的插件https://github.com/svermeulen/Unity3dAsyncAwaitUtil(需要科学上网)

用到.Net库中的一些API的时候可以考虑使用异步方法

用第三方插件实现的Resources.LoadAsync()实例:

using UnityEngine;/// <summary>
/// 用异步函数异步加载一个立方体到场景中
/// </summary>
public class Lesson6 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){LoadCubeAsync("cube");}/// <summary>/// 异步加载一个资源并实例化到场景中/// </summary>private async void LoadCubeAsync(string resName){print("开始加载一个资源");//获取异步加载请求ResourceRequest request = Resources.LoadAsync<GameObject>(resName);//等待资源异步加载完成await request;print("加载完毕,开始实例化");GameObject cube = request.asset as GameObject;cube = Instantiate(cube, Vector3.zero, Quaternion.identity);}
}

①用来这插件之后可以让await支持对ResourceRequest这个类的异步等待

②之后按异步方法的常规套路来处理等待加载完成之后的逻辑


四.总结

①Unity各个版本支持的C#版本是不同的,因为各个版本的Unity使用的脚本运行时和C#编译器都是更新的,这能够让我们使用最新的C#来在我们的代码中进行编程

②更新版本的C#一般会带来新的方法和特性,使用这些方法和特性往往能够让我们的代码更加简洁

③在C#4中,加入了dynamic动态类型,能够让我们绕过编译器检查去在运行的时候动态地生成相应类型的变量,这在我们无法一开始确定对象的类型的时候还是比较有用的。但是它不支持IL2CPP,且需要把.Net兼容版本设置为.Net Framework,这意味着更大的包体,更低的执行效率,以及放弃了跨平台的特性。因此我们一般不推荐使用这个

④在C#5中,加入了ThreadPool,Task,和异步方法Async与await关键字

⑤ThreadPool能够帮助我们更高效地利用线程资源,但是无法精确地获取每个线程的信息与控制

⑥Task是基于ThreadPool的优点的更高一层封装,它能够创建无返回值和有返回值的任务类型,并且能够让任务同步执行(用new方法创建的),让关键任务进行阻塞,让几个任务按一定顺序完成,同时可以用一个取消标记源类来控制任务的撤销以及触发相应的撤销回调

⑦Async和await关键字合作来完成对一个异步方法的修饰,Async修饰的方法只能够以void,Task,Task<>作为返回值await是用于挂起该函数并等待一个线程逻辑执行完毕之后再执行后续的函数逻辑,一个函数中可以包含多个await。

⑧异步方法通常用于复杂计算,计时器,资源加载等场景。其中Unity内置的一些资源加载的异步方法需要加入一些第三方插件才能够支持运行。不过一般情况下用协同函数也能够完成这样的资源加载。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

水闸安全综合监测系统解决方案

一、方案概述 水闸作为重要的水利工程设施&#xff0c;承担着防洪、排涝、供水和灌溉等关键功能。其安全性直接关系到下游人民群众的生命财产安全以及区域经济的稳定发展。近年来&#xff0c;随着极端天气频发和工程老化问题日益突出&#xff0c;水闸安全监测工作显得尤为重要。…

基于单片机智能点滴输液系统

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 该系统基于单片机控制技术&#xff0c;结合传感器和无线通信模块&#xff0c;实现对输液过程的实…

AI数据仓库管理提升效率

内容概要在数字化转型浪潮中&#xff0c;AI数据仓库管理正重塑企业数据处理格局。本部分简要介绍其核心机制&#xff0c;即通过智能API接入外部数据源实现多平台数据无缝整合&#xff0c;随后应用数据清洗技术去除冗余信息&#xff0c;确保数据质量。同时&#xff0c;加密存储机…

使用 Docker 安装长安链管理平台 + 部署区块链与示例合约

文章目录简介登录官网GithubPodman 配置&#xff08;Docker 配置 registry 地址&#xff09;安装长安链管理平台下载源码docker-compose.yml登录管理平台部署区块链Dockerfile构建镜像部署长安链订阅区块链部署合约下载示例合约部署示例合约投票管理文件哈希存证查找存证信息区…

Python训练营打卡 DAY 41 简单CNN

知识回顾 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常见流程如下&#xff1a; 1. 输入 → 卷积层 → Batch归一化层…

云端赋能,智慧运维:分布式光伏电站一体化监控平台研究

摘要 本文针对分布式光伏电站存在的监管困难、火灾隐患、系统繁杂及运维不规范等行业痛点&#xff0c;提出AcrelCloud-1200光伏运维云平台解决方案。平台通过ANet-1E2S-4G网关集成多品牌逆变器数据&#xff0c;结合视频监控与气象站&#xff0c;实现电站全域监测&#xff1b;开…

CVPR 2025 | 具身智能 | HOLODECK:一句话召唤3D世界,智能体的“元宇宙练功房”来了

关注gongzhonghao【CVPR顶会精选】1.导读1.1 论文基本信息论文标题&#xff1a;《HOLODECK: Language Guided Generation of 3D Embodied AI Environments》作者&#xff1a;Yue Yang*1, Fan-Yun Sun*2, Luca Weihs*4, Eli Vanderbilt4, Alvaro Herrasti4,Winson Han4, Jiajun …

迅为RK3568开发板搭建Ubuntu环境

本小节介绍开发所需 Ubuntu 环境的搭建方法。系统要求:Ubuntu 系统要求&#xff1a;Ubuntu18.04~21.10 版本。推荐使用 20.04 版本&#xff0c;内存 16GB 及以上&#xff0c;硬盘 100GB 及以上。Ubuntu 系统的用户名不能包含中文字符。建议 Ubuntu 和 Windows 系统上安装的 Dev…

【数据结构】用堆解决TOPK问题

设计一个算法&#xff0c;找出数组中最小的k个数。以任意顺序返回这k个数均可。示例&#xff1a;输入&#xff1a; arr [1,3,5,7,2,4,6,8], k 4 输出&#xff1a; [1,2,3,4]比较替换堆顶的数时&#xff0c;不需要让堆顶与数组的每一个数再进行比较&#xff0c;比较数组减去k个…

【深度长文】Anthropic发布Prompt Engineering全新指南

目录 1.什么时候适合用提示工程? 2.如何进行提示工程 2.1 使用提示模板 2.1.1 使用提示模板和变量 2.1.2 何时使用提示模板和变量 2.1.3 提示模板示例 2.2 保持清晰和直接 2.2.1 如何保持清晰、具有上下文和具体 2.2.2 示例 ​2.3 使用示例&#xff08;多示例提示…

【基础-判断】HarmonyOS提供了基础的应用加固安全能力,包括混淆、加密和代码签名能力

正确 解释如下: 应用加固: 这是指对应用程序进行保护,使其更难被逆向工程、篡改或盗版。HarmonyOS 作为现代操作系统,确实提供了这样的基础安全能力。 混淆: HarmonyOS 的 SDK 提供了代码混淆工具(通常基于 ProGuard 或类似技术)。开发者在构建应用时启用混淆,可以将类…

HTML 框架:构建网页布局的基石

HTML 框架&#xff1a;构建网页布局的基石 引言 HTML 框架是网页设计中不可或缺的一部分&#xff0c;它为网页内容的布局提供了强大的支持。本文将深入探讨 HTML 框架的概念、种类、应用以及如何有效地使用它们来构建网页布局。 什么是 HTML 框架&#xff1f; HTML 框架是一种网…

[Linux]学习笔记系列 -- [mm][memblock]

文章目录mm/memblock.c: Linux内核的“拓荒时代”内存管理器一、 核心问题&#xff1a;为什么需要 memblock&#xff1f;二、 核心原理与设计三、 在内核启动流程中的角色四、 关键 API五、 总结include/linux/memblock.hmm/memblock.cmemblock_reserve 预留内存块for_each_mem…

Java 面试八股文汇总(1000 道附答案解析)

在过 2 个月即将进入金九银十了&#xff0c;然而面对今年的大环境而言&#xff0c;跳槽成功的难度比往年高了很多&#xff0c;很明显的感受就是&#xff1a;对于今年的 java 开发朋友跳槽面试&#xff0c;无论一面还是二面&#xff0c;都开始考验一个 Java 程序员的技术功底和基…

给纯小白的Python操作 PDF 笔记

一、文件基础打开与关闭 推荐用 with open(path, mode, encodingutf-8) as f:&#xff0c;自动完成 close()&#xff0c;避免泄露文件句柄。常见模式&#xff1a;r 读&#xff0c;w 写覆盖&#xff0c;a 追加&#xff0c;rb/wb 二进制。Windows 默认编码为 GBK&#xff0c;Linu…

vue使用vue-cropper实现图片裁剪之单图裁剪

vue制作的pc系统中(如若依系统)&#xff0c;需要实现按照固定尺寸进行裁剪后再进行图片上传&#xff0c;以下代码讲述的是实现单张图片裁剪上传。1.第一步需要安装vue-croppernpm install vue-cropper2.第二步在需要的页面进入代码引入import {VueCropper} from "vue-crop…

后台管理系统-5-vue3之子路由渲染首页及卡片容器和表格容器实现

文章目录 1 子路由的实现 1.1 router/index.js 1.2 views/Home.vue(首页) 1.3 Main.vue 2 左上方的卡片 2.1 分栏间隔(Layout布局) 2.2 卡片容器(el-card) 2.3 整体代码Home.vue 3 左下方的table(静态实现) 3.1 准备数据 3.2 渲染表格(el-table) 3.3 整体代码Home.vue 4 附录 子…

在CentOS系统中查询已删除但仍占用磁盘空间的文件

在CentOS系统中查询已删除但仍占用磁盘空间的文件在CentOS系统中查询已删除但仍占用磁盘空间的文件1. 检查磁盘整体使用情况2. 查找被删除但仍被进程占用的文件3. 释放磁盘空间4. 替代方案&#xff08;不终止进程&#xff09;注意事项补充工具在CentOS系统中查询已删除但仍占用…

正点原子【第四期】Linux之驱动开发学习笔记-1.1 Linux驱动开发与裸机开发的区别

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子【第四期】手把手教你学Linux系列课程之 Linux驱动开发篇”视频的学习笔记&#xff0c;该课程配套开发板为正点原子alpha/mini Linux开发板。在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内…

Android SystemServer 中 Service 的创建和启动方式

今天导师给我将讲了一些如何新建一个系统服务&#xff0c;以及如何去初始化。 Android SystemServer 中 Service 的创建和启动方式 在 Android 系统中&#xff0c;SystemServer 是系统服务的核心进程&#xff0c;负责启动和管理各种系统服务。以下是 SystemServer 中服务创建和…