C#笔记################################################################################################

Python首选pywinauto,C#选FlaUI,避坑图像识别 优先选用UIA对象识别,确保稳定与低维护成本;仅当控件无法通过UIA获取时,才使用图像/OCR兜底。Python团队首选pywinauto,C#/.NET团队推荐FlaUI(支持WPF/WinUI3,活跃维护);Appium生态可选NovaWindowsDriver替代已停更的WinAppDriver;图像方案如SikuliX/AirTest适用于特殊场景,但需警惕分辨率与DPI问题。避免过度依赖图像识别,高DPI环境需选支持感知的工具。 阅读详情

各数据类型的字节数

byte、sbyte、bool1
short、ushort、char2
int、uint、float4
long、ulong、double8
string(栈内存的引用地址)32位4,64位8

char使用unicode编码,可以表示汉字

char a='的';

负整数、补码

负整数用正整数的补码表示。

定义:比特定位数二进制数的最大值大1的数减去一个数。

快速算法:从右向左找到第一个1‌,保留该1及其右侧所有位,‌左侧全部取反。

小数的二进制表示

根据IEEE 754标准,32位单精度浮点数(float)的二进制表示包含以下三部分:

符号位(Sign)‌

1位,最高位(第31位)
0表示正数,1表示负数

指数位(Exponent)‌

8位(第30-23位)
采用偏移码表示,实际指数值=存储值-127
例如指数3存储为127+3=130(二进制10000010)

尾数位(Mantissa)‌

23位(第22-0位)
存储规格化后的有效数字小数部分,隐含整数部分1
例如1.1011的尾数存储为101100...0

转换步骤示例(以8.25为例)‌:

转为二进制:8.25 → 1000.01
规格化:1.00001×2³
组合三部分:
符号位:0(正数)
指数位:3+127=130 → 10000010
尾数位:000010...0(补足23位)
最终二进制:0 10000010 00001000000000000000000

特殊值处理:

指数全0:表示0或非规格化数
指数全1:表示无穷大或NaN

64位双精度浮点数(double)采用类似结构,但指数位11位(偏移1023),尾数位52位。

字符串

字符串开头的@

  1. 不使用转义字符;
  2. 允许字符串换行;

函数

参数前面的in

在方法参数中使用in表示该参数是只读引用,方法内部不能修改这个参数的值,同时避免了复制大型结构体的开销。类似C++ const XXX&。

结构体

结构体是值类型,为什么还需要new,直接写MyStruct a行吗?

在C#中,结构体是值类型,可以直接声明变量后使用,但使用new关键字有不同的作用。

直接声明结构体变量是允许的,此时结构体的所有字段都保持未赋值状态,需要在使用前手动为每个字段赋值,否则编译器会报错。例如:

csharp
Copy Code
MyStruct a;
a.Field1 = 10; // 必须在使用前赋值
使用new关键字实例化结构体会调用适当的构造函数,将结构体成员初始化为它们的默认值。例如,对于包含int类型的字段,会被初始化为0,这样创建的结构体对象可以直接使用而不会出现编译错误。

因此,两种方式的主要区别在于初始化时机:直接声明需要手动初始化,而使用new会通过构造函数自动初始化。结构体作为值类型,其内存通常分配在栈上,这与引用类型的类在堆上分配内存不同

int?的意思是

底层是

public struct Nullable<T>{
    public bool HasValue;
    public T Value;
}

对int就是

public struct Nullable<int>{
    public bool HasValue;
    public int Value;
}

为null相当于HasValue=false。

结构体和类在foreach中的区别

循环项如果是结构体则不允许赋值以及修改任何成员,是类则可以修改成员,只是不能整体赋值。

面向对象

private的可访问范围

可以在一个A类对象a1的函数内访问另一个A类对象a2的private成员。private意思是只有本类的任意对象内可访问本成员,而不限于本对象!!!!!!!!!!!!!!!!!

internal class Person
{
    private int id;
    public Person(int id)
    {
        this.id = id;
    }
    public void PrintOtherID(Person other)
    {
        Console.WriteLine(other.id);
    }
}

字段=>

表示只读属性。

继承

父类有静态变量,子类继承时,读写的还是父类那个静态变量。

public class Father{
    protected static int a=0;
}
public class Son1:Father{
    a=441;
}
public class Son2:Father{
    a=123;
}

父类是泛型类,子类继承时把自己传入,不同子类使用的是不同静态变量。

public class Father<T>{
    protected static int a=0;
}
public class Son1:Father<Son1>{
    a=441;
}
public class Son2:Father<Son2>{
    a=123;
}

对象引用里除了指针还有什么?

在C#中,对象引用(引用类型)的核心机制除了包含指向堆内存的指针外,还涉及以下关键组成部分:

1. 类型元数据

引用类型通过类型对象指针(Type Object Pointer)关联到其类型元数据,包括类结构、方法表、接口实现等信息。这些元数据由CLR管理,用于运行时类型检查和方法分派。

2. 同步块索引

每个引用类型对象头部包含同步块索引(Sync Block Index),用于支持线程同步机制(如lock语句)。该索引指向同步块表中的一个条目,存储锁状态和哈希码等信息。

3. 对象头开销

引用类型实例在堆中分配时会有额外的对象头开销(Object Header),包含类型指针和同步块索引等元信息。这部分对开发者透明,但影响内存布局。

4. 可空性支持

引用类型天然支持null值,这是区别于值类型的重要特性。null表示引用不指向任何有效对象,而值类型需通过Nullable<T>实现类似功能。

5. 继承与多态

引用类型通过虚方法表(VTable)实现继承和多态行为,这是指针之外的动态调度机制。所有引用类型隐式继承自System.Object,具备ToString()等基础方法。

6. 垃圾回收标记

CLR的垃圾回收器会通过引用跟踪对象可达性,引用本身可能被GC优化或重定位,这与原始指针的固定地址特性不同。

注意:C#中的引用是托管概念,不同于C++的裸指针,它由CLR统一管理并包含上述附加信息,确保类型安全和内存自动回收。

成员对象要不要直接写=new()

取决于你设计这个成员是否会为空。比如背包的步枪栏,如果要通过rifle==null判断没有步枪,则不写new(),而背包的物品列表,我们希望背包没有物品时它是一个没有元素的列表,而非null,那么定义时就写上new()。

基类的静态成员由子类共用还是各有一个

非泛型基类,子类共用一个静态成员。泛型基类,子类有各自的静态成员。因为泛型基类不是一个完整的类,本身不能使用,每填入一个类型,就实现一个独立的类。单例基类就是例子。

自定义隐式类型转换public static implicit operator

public static implicit operator Vector3(Vector3Data rValue)
{
    return new Vector3(rValue.x, rValue.y, rValue.z);
}

自定义一个和Vector3能直接=隐式转换的三维向量

public struct MyVec3
{
    public float x, y, z;
    public static implicit operator Vector3(MyVec3 v)
    {
        return new Vector3(v.x, v.y, v.z);
    }
    public static implicit operator MyVec3(Vector3 v)
    {
        return new MyVec3()
        {
            x = v.x,
            y = v.y,
            z = v.z
        };
    }
}

接口

接口中的抽象方法不用写abstract,类的实现也不用写override。

出现菱形继承时,孙类的实现方法直接通过方法所属的接口.方法实现,方法是爷类的就写爷类,是哪个父类就写哪个父类。类似于直接使用C++虚继承。

public interface GP
{
    public void Ha();
}
public interface P1 : GP
{

}
public interface P2 : GP
{

}
public class Son : P1, P2
{
    void GP.Ha()
    {
        throw new System.NotImplementedException();
    }
}

显式实现接口

实现方法时,写接口名.方法。用于继承的两个接口的方法同名时。这个方法不能加访问修饰符。不能被实现的类调用,只能用接口装对象,调用。如果接口是自己写的,不要定义同名方法。

扩展方法

当对象是空时希望扩展方法直接返回空,但是空对象调用方法会报错?

对象。即使是空,调用扩展方法也不会报错。

列表

FindIndex()输入一个lambda表达式,查找符合条件的元素的索引。

// 正确用法
List<int> numbers = new List<int> { 1, 3, 5, 7, 9 };
int index1 = numbers.FindIndex(x => x == 5);  // 返回 2
int index2 = numbers.FindIndex(x => x > 6);   // 返回 3

字典

可以给字典不存在的键直接赋值吗?

在C#中,可以直接对Dictionary中不存在的键进行赋值操作,此时会自动添加新的键值对而不会抛出异常。

使用索引器为Dictionary赋值时,如果指定的键不存在,系统会自动添加一个新的键值对;如果键已存在,则会更新对应的值。这一行为与一些开发者的预期可能不同,他们认为在键不存在时应抛出KeyNotFoundException异常。

如果需要严格区分添加和更新操作,可以使用Add方法添加新元素(键存在时会抛出异常),或使用ContainsKey方法先检查键是否存在。此外,ConcurrentDictionary类型还提供了AddOrUpdate方法,能够更明确地处理键存在与否的情况。

字典内部:int[] _buckets和Entry[] _entries数组

字典内部有2个数组int[] _buckets和Entry[] _entries,长度相同。(期望每个数据的哈希值都不同,虽然实际无法实现)

根据键计算出哈希值h,来到_buckets[h],得到一个整数,就是本组数据的第一个在_entries的索引。然后一个Entry里记录了本组下一个数据在_entries的索引。

struct Entry {
    public int hashCode; // 哈希码(低31位)
    public int next;     // 冲突链的下一个节点索引(-1 表示结尾)
    public TKey key;
    public TValue value;
}

当元素个数>=数组长度*0.72时,扩容为用来的2倍。

HashSet<>

重复Add同一个元素,相当于没有重复Add。不报错。

装委托时,不同对象的实例函数被视为不同的函数!

HashSet<UnityAction> set = new();
BuildConfig b1 = new();
BuildConfig b2 = new();
set.Add(b1.CheckResAB);
set.Add(b2.CheckResAB);
MyTools.Print(set.Count, Color.cyan);//打印2!

class Tuple和ValueTuple(也就是(T1,T2))

Tuple是引用类型,相等是判断内存地址,但是GetHashCode()好像是根据内容值计算,得到一个int,然后判断相等调用的是int的Equals(),所以用Tuple.Create()新创建的tuple查询字典能命中。

ValueTuple可简写成(类型1,类型2,……),是值类型,可以用来做多键字典。

委托、事件

触发可以用XXX()也可以XXX.Invoke();

输入参数、返回值类型都一样,类型不同的两个委托能赋值吗?

不能。

UnityAction ca=Update;
Action c2;
c2 = ca;

多播委托内部是怎么记录被添加的函数的?

C#多播委托内部通过‌调用链表(Invocation List)‌记录被添加的函数,其核心机制如下:

1. ‌调用链表结构‌
底层实现‌:多播委托继承自System.MulticastDelegate类,内部维护一个_invocationList字段(实际是object[]数组),用于存储多个方法引用。
数据结构‌:每个委托实例包含一个方法链表,链表中的每个元素代表一个方法的引用(包括方法地址和所属对象,如果是实例方法)。
2. ‌方法添加与合并‌
+= 操作符‌:通过+=操作符将新方法添加到链表末尾,生成新的委托实例,合并原有和新增方法。
链表构建‌:每次添加方法时,运行时会创建一个包含所有订阅方法的链表,按注册顺序排列。
3. ‌调用顺序与执行机制‌
执行流程‌:调用多播委托时,系统遍历链表并按顺序执行每个方法。
异常处理‌:若某个方法抛出异常,后续方法将不会执行。
4. ‌返回值处理‌
无返回值委托‌:所有方法按顺序执行,无返回值。
有返回值委托‌:仅返回最后一个方法的返回值。
5. ‌管理调用链‌
GetInvocationList()‌:可获取当前链表,手动控制执行顺序。
+= 和 -= 操作‌:用于添加或移除方法,影响调用顺序。
示例

public delegate void MyDelegate(string msg);
MyDelegate del = MethodA;
del += MethodB; // MethodB 添加到链表末尾
del("Hello"); // 按顺序执行 MethodA -> MethodB


总结‌:多播委托通过链表记录方法引用,按添加顺序执行,实现方法的动态组合与调用。

委托-=时这个链表发生了什么?

在C#中,当使用-=操作符从多播委托中移除方法时,其内部调用链表(Invocation List)会发生以下关键变化:

1. ‌链表重组机制‌
移除匹配项‌:委托会遍历调用链表,查找与待移除方法签名和对象引用完全匹配的项。若找到匹配项,则从链表中移除该方法引用。
链表重建‌:移除操作会生成一个新的调用链表,包含所有未被移除的方法。原链表保持不变,新链表按顺序存储剩余方法。
2. ‌移除失败的处理‌
静默失败‌:若未找到匹配项(如方法签名不一致或对象引用不同),移除操作无效且不抛出异常。链表保持原样,后续调用仍包含所有方法。
引用一致性要求‌:移除时必须精确匹配原始添加方法的引用(包括匿名方法或Lambda表达式),否则移除失败。
3. ‌调用链表的结构‌
单方法委托‌:若链表仅剩一个方法,委托退化为普通委托(非多播)。
多方法链表‌:链表按添加顺序存储方法引用,调用时依次执行。
4. ‌示例说明‌

Action del = MethodA;
del += MethodB; // 链表: [MethodA, MethodB]
del -= MethodA; // 链表: [MethodB](MethodA被移除)

总结‌:-=操作通过链表重组移除匹配方法,若匹配失败链表不变。理解链表机制是避免移除失败的关键。

会生成一个新的调用链表,包含所有未被移除的方法。原链表保持不变,也就是说会有2个链表?

是的,当使用-=操作符从多播委托中移除方法时,会生成一个新的调用链表(Invocation List),包含所有未被移除的方法。原链表保持不变,因此确实会有两个链表:

原链表‌:包含所有方法的原始链表,未发生任何变化。
新链表‌:包含移除指定方法后剩余的方法,按添加顺序排列。

这种机制确保了移除操作的原子性,避免了链表被破坏。调用链表的结构如下:

text
Copy Code
原链表: [MethodA, MethodB, MethodC]
移除 MethodB 后:
  新链表: [MethodA, MethodC]
  原链表: [MethodA, MethodB, MethodC](保持不变)


关键点‌:

移除操作生成新链表,原链表不变。
若移除失败(如方法签名不匹配),链表保持原样。
链表按添加顺序存储方法引用,调用时依次执行。

多播委托最多有几个链表?

在C#中,多播委托内部最多会维护‌两个链表‌:

原链表‌:包含所有方法的原始链表,未发生任何变化。
新链表‌:在执行-=操作时生成的新链表,包含移除指定方法后剩余的方法。
生成机制
链表重组‌:移除操作通过遍历原链表查找匹配项,生成包含剩余方法的新链表。
链表独立性‌:原链表保持不变,新链表按顺序存储剩余方法。
示例

Action del = MethodA;
del += MethodB; // 原链表: [MethodA, MethodB]
del -= MethodA; // 新链表: [MethodB], 原链表: [MethodA, MethodB]


总结‌:多播委托最多维护两个链表,原链表不变,新链表包含移除后剩余方法。

多播委托多次-=时,这两个链表怎么变化?

在C#中,多播委托多次使用-=操作符移除方法时,其内部调用链表(Invocation List)会按以下规律变化:

1. ‌链表重组机制‌
每次移除操作‌:生成一个新的调用链表,包含所有未被移除的方法。原链表保持不变,新链表按顺序存储剩余方法。
链表独立性‌:每次移除后,原链表和新链表是两个独立的链表结构。
2. ‌链表变化示例‌
csharp
Copy Code
Action del = MethodA;
del += MethodB; // 原链表: [MethodA, MethodB]
del -= MethodA; // 新链表: [MethodB], 原链表: [MethodA, MethodB]
del -= MethodB; // 新链表: [], 原链表: [MethodA, MethodB]

3. ‌最终状态‌
空链表‌:若所有方法被移除,最终链表为空(null),后续调用会抛出NullReferenceException。
4. ‌关键点‌
链表数量‌:每次移除操作生成一个新链表,链表数量随移除次数增加。
原链表不变‌:移除操作不影响原链表,始终保留初始方法列表。

总结‌:多播委托多次移除时,链表会生成多个独立链表,原链表保持不变。最终链表为空时需检查null。

多个类都用一个事件,把事件放到它们的基类,结果子类不能调用

那么要么每个子类放一个事件,要么用委托。

System.IO

File

bool File.Exists(path);

File.WriteAllText(path, str);

string File.ReadAllText(path)

Directory

Directory.Exists(path);

Directoriy.CreateDirectory(path);

string[] Directory.GetFiles(path,string 后缀);

Path.Combine("D:/MyProject2/Assets", "/Config/Missions.json");结果是

结果就是第二个参数。因为Path.Combine 的行为规则是:如果第二个参数以路径分隔符开头(/ 或 \),则直接返回第二个参数,忽略第一个参数。

反射

得到Type

typeof(类名);
实例.GetType();
Type.GetType("命名空间.类名");

每一个对象,都有一个地方记着它们的类型?

对,C# 里每个对象,头上都挂着自己的 “类型信息”。在 .NET 里,每个对象在内存里长成这样:

[ 对象头 ]  → 里面有指针指向
[ 类型对象指针 ]  ← 这就是关键!
[ 字段数据 ]

得到公共成员

MemberInfo memberInfos[]=type.GetMembers();

得到构造函数

得到所有:

ConstructorInfo[] constructorInfos=type.GetConstructors();

得到无参:

ConstructorInfo constructorInfo=type.GetConstructor(new Type[0]);
MyClass myClass=constructorInfo.Invoke(null) as MyClass;

得到有参:

ConstructorInfo constructorInfo2=type.GetConstructor(new Type[]{typeof(参数类型)});
myClass=constructorInfo2.Invoke(new object[]{2}) as MyClass;

Activator快速实例化

myClass=Activator.CreateInstance(type) as MyClass;

得到公共字段

按字段名得到:

FieldInfo fieldInfo=type.GetField(string 字段名);

得到所有字段

FieldInfo[] fieldInfos=type.GetFields();

公共字段读写

fieldInfo.GetValue(实例);
fieldInfo.SetValue(实例,数据);

得到公共方法

得到所有

MethodInfo[] methodInfos=type.GetMethods();

按方法名得到:

MethodInfo methodInfo=type.GetMethod(string 方法名,new Type[]{typeof(参数1类型),...});

执行公共方法

实例方法

methodInfo.Invoke(实例,new object[]{参数1,...});

静态方法

methodInfo.Invoke(null,new object[]{参数1,...});

判断一个type是列表

typeof(IList).IsAssignableFrom(type) && type.IsGenericType

 判断一个type是字典

if(typeof(IDictionary).IsAssignableFrom(type)){

}

把一个字符串转换成枚举type的值

enumValue = Enum.Parse(type, enumStr, ignoreCase: true);

获得List的Fields,什么都没得到

List<int>data=new List<int>(){123,14,4234};
Type dataType=data.GetType();
FieldInfo[] fieldInfos=dataType.GetFields();

正确用法:把List装到一个class里。

特性

理解特性

特性是反射的加强功能,在FieldInfo常用的名称、类型基础上给它加自定义特征,用于把一个类拆成fieldInfos后筛选出我们需要的字段。比如资源校验时,在string path上打上包含此资源的类型的特性,然后可以用反射+循环+检查特性检查类型或必要脚本。

定义特性

class MyAttribute:Attribute{
   
}

使用特性

type.IsDefined(typeof(),bool)判断一个类是否被加了某特性,第二个参数选择是否搜索目标类的父类。

type.GetCustomAttributes(false)得到一个类加的所有特性。

[My]
public class MyClass{

}
static void Main(){
    MyClass myClass=new MyClass();
    Type type=myClass.GetType();
    if(type.IsDefined(typeof(MyAttribute),false){
        Console.WriteLine("加了特性My");
    }
    object[] atts=type.GetCustomAttributes(false);
    for(int i=0;i<atts.Length;i++){
        
    }
}

很明显,type.IsDefined是个笨方法,对于m个类,n个特性,不可能去判断m*n次。最理想的是能获得添加某特性的所有类,比如对于[MenuItem]特性,就能一下汇总要加到菜单的类。

限制特性使用范围

Inherited:是否自动给子类加上此特性

[AttributeUsage(AttributeTargets.Class|Attributes.Field,AllowMultiple=false,Inherited=false)]
class MyAttribute:Attribute{
   
}

多线程、异步

Task

实例方法运行线程

Task t1=new Task(()=>{

});
t1.Start();

静态方法运行线程:

Task t2=Task.Run(()=>{

});

任务工厂运行线程

Task t3=Task.Factory.StartNew(()=>{

});

阻塞当前线程,直到task执行完:

task.Wait();

阻塞当前线程,直到至少一个参数Task执行完:

Task.Wait(t1,t2,t3);

Task<T>

线程可以返回一个T类型的值,通过task.Result得到结果。但是会阻塞所在线程,等Task线程执行完才能得到。

CancellationTokenSource

CancellationTokenSource cts=new CancellationTokenSource(3000);//取消时间
cts.Token.Register(()=>{});//取消后的回调
if(cts.IsCancellationRequested){
    //任务停止
}
cts.Cancel();//让IsCancellationRequested变成true

async await

  1. async函数只能返回void、Task、Task<>;
  2. await后面跟一个Task或Task<>,Task.Run()返回Task;

异步函数延时:

async void Timer(){
    //XXX
    await Task.Delay(1000);
    /YYY
}

自定义可以被await的类

  1. 有GetAwaiter();方法
  2. GetAwaiter();返回的类型要继承INotifyCompletion

内存泄漏的原因

C#内存泄漏的常见原因及分析如下:

1. 事件订阅未取消‌

事件处理程序(委托)会隐式持有订阅者对象的引用,若未显式取消订阅(-=),即使订阅者不再使用,仍会被发布者长期持有,导致无法被垃圾回收(GC)。常见于UI框架(如WPF)或长时间运行的服务中‌。
示例场景‌:窗口控件订阅系统事件后未取消,即使窗口关闭,事件仍保持引用。

2. 静态变量持有引用‌

静态变量生命周期与应用程序一致,若静态集合(如List、Dictionary)存储对象且未清理,这些对象将永久驻留内存。单例模式(Singleton)若持有大对象引用也可能导致泄漏‌。
典型问题‌:缓存类使用静态字典存储数据,但未设置过期策略或清理机制。

3. 非托管资源未释放‌

C#的GC仅管理托管内存,非托管资源(如文件句柄、数据库连接、Marshal.AllocHGlobal分配的内存)需通过IDisposable接口显式释放。未调用Dispose()或未使用using语句会导致资源泄漏‌。
关键点‌:FileStream、SqlConnection等必须显式关闭或包裹在using块中。

4. 匿名函数闭包引用‌

Lambda表达式或匿名函数捕获外部变量时,会形成闭包并延长变量生命周期。例如,控件构造函数中订阅事件并捕获this指针,即使控件销毁,闭包仍阻止GC回收‌。
解决方案‌:避免在匿名函数中捕获不必要的上下文变量。

5. 缓存管理不当‌

未限制缓存大小或未设置过期策略的缓存(如静态缓存)会无限增长,占用内存。例如,缓存未实现LRU(最近最少使用)策略时,旧数据堆积导致泄漏‌。

6. 第三方库或框架的隐藏泄漏‌

某些第三方库可能存在未公开的内存管理缺陷,如未正确释放事件订阅或资源句柄。需通过工具(如dotMemory)检测库的引用链‌。

7. 循环引用‌

对象间相互引用(如双向链表、图结构)可能导致GC无法回收。需使用弱引用(WeakReference)或手动断开引用链‌。

扩展阅读‌

总结‌:C#内存泄漏多源于引用管理不当或资源释放遗漏,需结合工具检测(如Visual Studio内存分析器)和编码规范(如IDisposable、弱引用)预防‌。

函数是何时被加载到内存中的?

在C#中,函数的加载时机与程序运行时的内存管理机制密切相关,主要分为以下阶段:

类加载阶段‌
当程序首次使用某个类时(如创建实例、访问静态成员等),该类及其所有成员(包括方法)会被加载到内存的‌代码区‌。静态函数在此时完成加载,且整个生命周期仅加载一次。

JIT编译阶段‌
函数的具体机器代码并非一开始就全部生成,而是在首次调用前由JIT(即时编译器)动态编译并存入内存。这种延迟编译机制优化了启动性能。

执行过程中的内存分配‌

栈帧管理‌:调用函数时,会在栈上创建栈帧,存储局部变量、参数和返回地址,调用结束后自动释放。
上下文保存‌:函数调用涉及寄存器和返回地址的压栈操作,确保执行流程可回溯。

动态代码场景‌
反射或动态生成的代码(如System.Reflection.Emit)会在运行时动态加载到内存,其函数加载时机由程序显式控制。

总结:C#函数加载是‌按需触发‌的过程,结合了类初始化、JIT编译和运行时栈管理的协同机制。

函数里声明一个局部数组,把它返回时是把数组元素都拷贝了一遍?

直接返回数组变量‌:此时返回的是数组引用而非拷贝,调用方和函数内部操作的是同一数组对象

依赖很多方法的方法不要轻易做成静态方法,否则底层方法一旦改成实例方法,整个调用链都要改

静态方法一般选比较底层的,只调用系统方法的做。

Aitoearn内容营销的最佳开源 AI 代理,创建 · 发布 · 互动 · 副业一站式平台,支持渠道:抖音、小红书(红点)、微信通道、快手、哔哩哔哩、微信公众号、抖音等等 阅读详情

相关推荐

2025虚幻游戏逆向工程解包尝试

2025虚幻游戏逆向工程解包指南 本文详细介绍了虚幻引擎5游戏逆向解包的完整流程,主要包括获取AES密钥、生成映射文件和导出资源三个关键步骤。作者在实际操作中遇到诸多难题:AES密钥获取工具存在加壳保护问题,映射文件生成器依赖复杂的VS编译环境,DLL注入工具的使用也存在技术门槛。通过多次尝试,作者发现部分游戏可直接搜索现成的AES密钥,而映射文件生成需要额外下载Memcury依赖库才能正常编译。文章提供了相关工具的下载链接和具体操作说明,但同时也指出每个环节都可能出现意外状况,逆向工程实际执行过程远比理论

wsdchong的博客 6142

【JVM】对象内存布局

HotSpot JVM 中,Java 对象内存布局由对象头(12字节,含Mark Word与压缩Klass Pointer)、实例数据及对齐填充构成,总大小为8字节倍数。对象头支持锁状态动态复用,指针压缩使引用仅4字节,但堆超32GB时失效。实例字段按类型优先级重排序以减少间隙,最小对象大小为16字节。通过JOL工具可精准验证布局,理解锁机制、GC、伪共享等底层行为。

2301_79732484的博客 139

ECMWF等大气数据批量下载(Python API)详细步骤

ECMWF等大气数据批量下载(Python API) 遥感方向做热红外温度反演,不可避免的要用到大气廓线数据,大气数据具有实时性,比如2018年9月1日的早上8点的影像,为了提高反演精度就需要用到时间上与其相差尽可能接近的大气数据,用于消除大气的影响,如果数据量较少我们可以查看影像的时间和空间范围进行下载,但是如果处理某个较大区域时可能涉及到大量的影像,如果一景一景对应查找去下载会很麻烦,因此针对大量数据的自动批量下载很有必要。本文根据 https://cds.climate.copernicus.eu/#

liuxiaojiangtobeno1的博客 1万+

Lua 与 ToLua:运行机制及热更新原理

本文解析 Lua 与 ToLua 在 Unity 中的运行机制及热更新原理。Lua 作为可嵌入语言,通过编译字节码由虚拟机执行,支持运行时加载新脚本;ToLua 实现 C# 与 Lua 的双向调用,通过生成 Wrap 代码注册类型接口;热更新依赖于宿主动态加载并执行新脚本,结合资源管理、版本控制与状态处理机制。关键点包括:table 共享引用、函数值不随重定义更新、闭包捕获状态、元表实现类逻辑,以及对象跨语言传递的生命周期管理。完整热更新需独立设计补丁分发与回滚策略。

2503_90324697的博客 125

UVM验证中,为什么我的pack_bytes()返回长度是0?手把手教你排查自定义do_pack函数

本文深入解析UVM验证中pack_bytes()返回长度为0的常见原因及系统排查方法。从UVM打包机制原理出发,详细列举了super.do_pack()缺失、field automation冲突等典型问题,并提供动态数组处理、位宽计算等进阶调试技巧,帮助开发者快速定位和解决自定义do_pack函数问题。

weixin_42523693的博客 127

TortoiseSVN的安装与使用

TortoiseSVN的安装与应用1、安装1.1、下载TortoiseSVN    点击下载TortoiseSvn1.2、下载汉化包。上面的页面中,找到Chinese, simplified汉化包,点击32位或者64位的Setup,下载汉化包。1.3、安装TortoiseSVN。在选择安装路径页面时要记得勾选“command line client tools”,默认不是勾选的。这里不勾选的话在I...

qq_28566071的博客 4万+

使用lxml操作 Word 的 OpenXML 底层

本文综合介绍了使用lxml操作Word底层OpenXML文档的基础知识,并给出了一个效率远超VBA的提取Word文档中的表格的HTML表结构的实例,演示了用lxml获取及修改部分Word文档组成部件的文本、属性的方法。

yivifu的专栏 386

48-OpenCVSharp —-Cv2.MatchTemplate() 函数功能(模板匹配)详解

是 OpenCV 中一个常见且简单的模板匹配算法,适用于目标检测和图像识别中的静态场景。它的优点是易于理解和实现,但在面对旋转、尺度变化、复杂背景或光照变化等挑战时,效果可能大打折扣。在这些情况下,结合多尺度图像、特征匹配算法(如 SIFT、SURF、ORB 等)以及深度学习模型可以显著提高匹配的鲁棒性和精度。模板匹配仍然是一个非常基础但强大的工具,适用于一些简单的图像匹配任务。对于更加复杂和动态的图像识别任务,我们通常会使用其他更加复杂的算法或者深度学习模型来处理。

weixin_45590420的博客 4522

C#使用opencv和Yolo模型识别车牌号

本文基于OpenCVSharp与OnnxRuntime,结合YoloV8n-pose_plate模型(检测车牌位置)和OCR模型(识别车牌文字),实现高效车牌识别。通过预处理、目标检测、NMS筛选及文字识别流程,最终输出车牌框位置与识别文本。代码封装良好,支持自定义路径与置信度阈值,具备实际应用价值。

lweiyue的专栏 151

利用Dify构建文档内容的分析对比

本次主要做了一些workflow和Agent的应用实现,供大家参考。主要目的是为了进行两个内部报告文件的内容对比,特别是报告中的各类审批表、申请表、推荐表和流程对比,本地数据通过知识库方式分别加载到两个知识库中,编制工作流针对同一个问题进行知识库检索,之后将查询结果进行合并,再一起提交给LLM进行对比,最终将对比分析结果以markdown方式返回显示。主要包括如何构建知识库,如何设计workflow、如何将工作流发布为工具、如何设计提示词、如何在agent中引入前面发布的工具,最终通过agent实现与用户交

hhue2007的专栏 2522

SAP 行业扫盲系列之一:SAP 从业者的分类

本文简单介绍了 SAP 从业者的分类,限于笔者狭隘的视角,可能存在遗漏或者不恰当的地方,希望大家在评论区指出。 那些在知乎咨询上向我提问关于进入 SAP 行业建议的朋友们,也请大家提问时注明自己想具体从事 SAP 行业哪一类的工作,这样才能得到更让你们满意的回复。感谢阅读。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1588

使用C#代码提取、修改或删除 Excel 中的超链接

通过 C# 和 VB.NET,可以方便地管理 Excel 文件中的超链接,包括提取、修改和删除等操作。提取超链接: 获取工作表中的超链接集合,读取每个超链接的地址和类型,并将相关信息保存到文本文件中。修改超链接: 更新超链接的显示文本和目标地址,确保链接信息准确。删除超链接: 移除指定的超链接,同时可以选择保留单元格中的原有显示文本,或清除单元格的全部内容。这些操作可以帮助开发人员更高效地管理 Excel 文档中的链接信息,减少重复的手动操作,也便于在数据迁移、文档维护和自动化报表处理等场景中使用。

2501_93070778的博客 219

使用GitLab限制开发者使用CI/CD的权限,三种方案

使用三种方案来限制开发人员的CI/CD权限。

知无涯 5095

C#: 控件不支持透明的背景色

把透明度固定为255。

海天鹰 23

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

week11-词云图

核心逻辑:词频越高,文字字号越大;适合快速看文本高频关键词(影评、评论、摘要文本非常适配 CommentInfo.csv)

m0_66244637的博客 131

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

基于 OPC UA 的半导体工厂化学气体传输 SCADA 监控系统:架构设计与 C# MVVM 实现

本文提出一套面向半导体化学气体传输的可演进SCADA系统,基于OPCUA统一信息模型与C#+WPF+MVVM技术栈,实现从气源到机台的全链路实时监控、安全联锁与追溯。通过资产树建模、分层解耦架构与标准对齐(SEMI E30/E95/S2),确保系统可扩展、可维护、可审计,满足高安全、高可靠要求,支持快速落地与持续演进。

ponygame的博客 306

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

C# 基于OpenCv的视觉工作流-章115-单节点条件

本章介绍C#基于OpenCv的视觉工作流中单节点条件的设置,涵盖列表值范围判断(支持“与”“或”逻辑)、模块结果参数匹配判定,以及结果显示图像与字体配置。通过灵活设定条件,实现检测结果的OK/NG自动判定,提升视觉系统智能化水平。配套工具“VisionTool 探迹”已更新,新增海康相机、PC相机采集及多类通讯优化功能,助力工业视觉应用。

sali-tec的博客 239

C# 程序一键打包(PupNet-Deploy)

它允许开发者通过一个统一的配置文件(.pupnet.conf)和一个简单的命令行指令,自动完成 .NET 代码编译(dotnet publish)与目标平台原生安装包(如 Windows 的 Setup 安装程序、Linux 的 AppImage / Debian / RPM 包等)的封装。一键全自动: 完美打通了“源码编译 -> 依赖收集 -> 路径替换 -> 快捷方式/元数据注册 -> 原生安装包生成”的全套自动化流水线。一次配置多端适配: 只需维护一份配置文件,即可搞定多端发布。

czjnoe的博客 177

把本地 docx 半自动发到 优快云 创作页(python实现)

本文介绍一套基于Python的自动化工具链,实现将本地Word文档(.docx)半自动发布至优快云创作页。通过docx2md.py将文档转为Markdown并导出图片,再用auto_upload.py自动粘贴文本与图片至网页编辑器,全程仅需双击脚本和点击输入框,支持40张图片按序粘贴,显著提升内容发布效率。

weixin_45615730的博客 192

04 · Diagram —— 示意图与接线图

CCDiagramDiagramDiagrams把一组"节点 + 连线"画出来:机车各柜体之间的连线关系、系统拓扑、信号流向。节点是矩形框(可带标题),连线可以带箭头。

hdjsjb的博客 435

MIGraphX CSharp API 发布:让 C# 接入 AMD GPU 原生推理

MIGraphX CSharp API 0.0.2 已发布,为 .NET 开发者提供 AMD GPU 上的 ONNX 模型推理能力。支持 YOLOv26、PaddleOCR、PP-Structure 与 CLIP 四类场景,实现模型解析、动态输入、GPU 编译与张量管理。通过类型化接口与 IDisposable 资源管控,兼容 .NET 5–10 及旧版框架,可集成 HIP 实现端到端 GPU 推理。配套案例覆盖视觉检测、文档处理与图文检索,助力 C# 项目高效接入 AMD ROCm 生态。

grape_yan的博客 349

C#: (float) RegistryKey.GetValue() 报错 System.InvalidCastException: 指定的转换无效

RegistryKey.GetValue() 类型是 object,首先要转为 string,再 float.Parse(string)。

海天鹰 27

C# 14 实战三连:field 关键字、extension 成员与 null 条件赋值

NET 10 带来的 C# 14 不是一场 syntax earthquake,但里面藏着三件日常写码频率极高的改进。这篇不讲新闻稿,只讲用起来什么感觉、编译器在背后做了什么、以及哪里会静默踩坑。

dotnethello的博客 159

OnlyOffice+poi-tl:把 Word 模板变成可填写、可生成的合同系统

合同表单已经填过主体、金额和日期,为什么还要在Word里再改一遍?以RuoYi Office的真实教学案例,拆解七个字段从模板配置到正文生成、在线编辑和回存的过程,说明poi-tl与OnlyOffice如何分工,以及字段取值、文件缓存和模板版本中的关键设计。

RuoYi Office是基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue3 + Vben Admin + Uniapp 构建的中小企业全业务办公一体化平台,涵盖OA 协同 458

C# 泛型

本文系统介绍.NET Core中泛型机制,涵盖泛型方法、类、接口与委托的定义与应用,强调类型安全与性能优势。通过泛型约束实现灵活控制,结合CLR的JIT编译机制,实现代码复用与高效执行。扩展泛型缓存提升ASP.NET Core开发体验,协变与逆变则增强接口与委托的灵活性。泛型是.NET核心特性,显著提升代码可维护性与运行效率。

cccloveforever的博客 503

C# 委托与事件

在.NET Core中,委托是实现灵活、低耦合架构的核心机制。它作为类型安全的函数指针,支持方法的传递与调用,广泛应用于事件驱动、回调、策略模式等场景。通过委托可实现模块解耦,提升代码可维护性;多播委托支持多个方法链式执行,便于构建观察者模式。事件是对委托的封装,通过event关键字限制外部直接操作,保障通知机制的安全性。合理使用委托与事件,能有效降低系统耦合度,增强扩展性,是构建高效、可维护应用的关键基础。

cccloveforever的博客 586
上一篇: 算法题笔记
下一篇: C++笔记
WarPigs
博客等级 码龄7年 248粉丝 175原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值