文章目录

  • 一、类的访问
    • 1、普通类继承抽象类
    • 2、普通类继承抽象类,抽象类继承接口,三者联系
  • 二、类中方法的访问
    • 2.1 抽象方法和虚方法
    • 2.2 虚方法和普通方法
      • **1. 调用机制**
      • **2. 方法重写**
      • **3. 设计意图**
      • **4. 性能差异**
      • **5. 语法对比表**
      • **总结:何时使用?**
  • 三、迭代器的使用
    • 3.1、使用场景及示例
    • 3.2、
  • 四、深度复制与浅度复制
    • 4.1、解析及示例
      • 1. **浅度复制(Shallow Copy)**
      • 2. **深度复制(Deep Copy)**
  • 五、引用和值类型
      • 一、值类型(Value Types)
      • 二、引用类型(Reference Types)

一、类的访问

在这里插入图片描述

1、普通类继承抽象类

在C#里,普通类继承抽象类时,有以下这些要点需要留意:

1. 必须实现所有抽象成员
抽象类中的抽象方法和属性不具备实现代码,继承它的普通类得把这些抽象成员全部实现出来。实现时,方法签名要和抽象类中定义的保持一致,并且要用override关键字。

public abstract class Shape
{public abstract double Area(); // 抽象方法
}public class Circle : Shape
{public double Radius { get; set; }// 实现抽象方法public override double Area() => Math.PI * Radius * Radius;
}

2. 遵循访问修饰符的限制
在实现抽象成员时,访问修饰符要和抽象类中定义的一样。比如,抽象类里的抽象方法是protected,那么派生类中实现该方法时也得用protected

3. 不能直接实例化抽象类
抽象类没办法直接创建实例,必须通过派生类来实例化。

Shape shape = new Circle { Radius = 5 }; // 正确
Shape shape = new Shape(); // 错误,无法实例化抽象类

4. 可以添加新成员
继承抽象类的普通类能够新增自己的字段、属性、方法或者事件。

public class Rectangle : Shape
{public double Width { get; set; }public double Height { get; set; }public override double Area() => Width * Height;// 新增方法public double Perimeter() => 2 * (Width + Height);
}

5. 抽象类可以包含非抽象成员
抽象类中除了抽象成员,还能有已经实现的方法、属性等,派生类可以直接继承或者重写这些非抽象成员。

public abstract class Animal
{public string Name { get; set; }public void Eat() => Console.WriteLine($"{Name} is eating."); // 非抽象方法public abstract void MakeSound(); // 抽象方法
}public class Dog : Animal
{public override void MakeSound() => Console.WriteLine("Woof!");
}

6. 抽象类也能继承自其他类或抽象类
如果抽象类继承了另一个抽象类,它可以选择实现部分抽象成员,剩下的由派生类去实现。

public abstract class Vehicle
{public abstract void Start();
}public abstract class Car : Vehicle
{public override void Start() => Console.WriteLine("Car started."); // 实现基类的抽象方法public abstract void Drive(); // 定义新的抽象方法
}public class SportsCar : Car
{public override void Drive() => Console.WriteLine("Sports car is driving fast.");
}

7. 不能用 sealed 修饰派生类
因为普通类要实现抽象类的抽象成员,所以不能用sealed关键字修饰该普通类,不然就没办法被其他类继承了。

总结
普通类继承抽象类时,要实现所有抽象成员,遵循访问修饰符的规则,不能实例化抽象类,不过可以添加新成员。抽象类可以有非抽象成员,还能继承其他类或抽象类。

2、普通类继承抽象类,抽象类继承接口,三者联系

当一个类(派生类)继承抽象基类,而抽象基类又实现了接口时,三者的成员函数关系遵循以下规则(以C#为例):

1. 接口定义“契约”,抽象基类部分或全部实现,派生类完成剩余实现

  • 接口:定义必须实现的成员(如方法、属性),但不提供实现。
  • 抽象基类
    • 必须“声明”实现接口的所有成员(即使只实现部分)。
    • 可将部分接口成员标记为 abstract(延迟到派生类实现),其他成员提供具体实现。
  • 派生类
    • 必须实现抽象基类中标记为 abstract 的接口成员(若有)。
    • 可选择重写(override)抽象基类中已实现的接口成员(若为 virtual)。

2. 示例说明
假设存在以下结构:

// 接口定义
public interface IMyInterface
{void MethodA();  // 接口方法void MethodB();
}// 抽象基类实现接口
public abstract class MyAbstractBase : IMyInterface
{public void MethodA()  // 具体实现接口方法{Console.WriteLine("Base.MethodA");}public abstract void MethodB();  // 抽象方法,延迟到派生类实现
}// 派生类继承抽象基类
public class MyDerivedClass : MyAbstractBase
{public override void MethodB()  // 实现抽象基类的抽象方法{Console.WriteLine("Derived.MethodB");}
}

成员关系分析

  • 接口 IMyInterface:定义 MethodA()MethodB()
  • 抽象基类 MyAbstractBase
    • 实现 MethodA(),派生类可直接使用。
    • MethodB() 标记为 abstract,强制派生类实现。
  • 派生类 MyDerivedClass
    • 无需关心 MethodA()(已由基类实现)。
    • 必须实现 MethodB(),否则会编译错误。

3. 特殊情况:抽象基类未完全实现接口
若抽象基类未实现接口的所有成员(即部分接口成员未被标记为 abstract 且未提供实现),则会导致编译错误。例如:

public abstract class MyAbstractBase : IMyInterface
{// 错误:未实现 MethodB(),且未声明为 abstractpublic void MethodA() { }
}

修正方式

  • MethodB() 声明为 abstract(如示例所示)。
  • 或在抽象基类中提供 MethodB() 的具体实现。

4. 接口显式实现与隐式实现
抽象基类可选择显式实现接口(只能通过接口类型调用):

public abstract class MyAbstractBase : IMyInterface
{void IMyInterface.MethodA()  // 显式实现接口方法{Console.WriteLine("Explicit implementation");}public abstract void MethodB();
}

此时,派生类需通过接口类型调用 MethodA()

MyDerivedClass derived = new MyDerivedClass();
((IMyInterface)derived).MethodA();  // 必须转型为接口类型

5. 派生类重写基类的实现
若抽象基类的方法为 virtual,派生类可选择重写:

public abstract class MyAbstractBase : IMyInterface
{public virtual void MethodA() { }  // 虚拟方法public abstract void MethodB();
}public class MyDerivedClass : MyAbstractBase
{public override void MethodA() { }  // 重写基类方法public override void MethodB() { }  // 实现抽象方法
}

6. 多层继承的扩展
若存在多层继承(如抽象基类继承自另一个抽象基类),规则相同:

  • 每个抽象基类可实现部分接口成员,剩余抽象成员由最终派生类实现。
  • 示例:
    public interface IMyInterface { void MethodA(); }
    public abstract class Base1 : IMyInterface { public abstract void MethodA(); }
    public abstract class Base2 : Base1 { }  // 未实现 MethodA(),仍为抽象类
    public class Derived : Base2 { public override void MethodA() { } }  // 最终实现
    

总结

角色对接口成员的责任对抽象成员的责任
接口定义所有成员签名
抽象基类必须声明实现所有接口成员(部分或全部实现)可定义抽象成员,强制派生类实现
派生类实现抽象基类中未实现的接口成员(即抽象成员)必须实现基类的所有抽象成员

这种分层设计允许:

  • 接口 定义统一契约。
  • 抽象基类 复用通用逻辑,简化派生类实现。
  • 派生类 专注于核心差异化逻辑。

二、类中方法的访问

在这里插入图片描述

2.1 抽象方法和虚方法

在C#中,抽象方法虚方法都用于实现多态性,但它们的设计目的和使用方式有本质区别。以下是两者的核心差异:

1. 定义语法与强制实现

抽象方法虚方法
使用 abstract 关键字声明,且不能有方法体
csharp<br>public abstract void Print();<br>
使用 virtual 关键字声明,必须有默认实现
csharp<br>public virtual void Print() { Console.WriteLine("Base"); }<br>
必须由派生类实现,否则派生类必须声明为抽象类。派生类可以选择是否重写,不重写时将继承基类的默认实现。

2. 所在类的限制

  • 抽象方法:只能存在于抽象类中(即使用 abstract 修饰的类)。
  • 虚方法:可以存在于普通类抽象类中。

3. 重写要求

抽象方法虚方法
派生类必须使用 override 关键字实现,且不能使用 newsealed 隐藏基类方法派生类使用 override 关键字重写(推荐),或使用 new 关键字隐藏基类方法(不推荐)。
示例:
csharp<br>public override void Print() { ... }<br>
示例:
csharp<br>public override void Print() { ... } // 重写<br>public new void Print() { ... } // 隐藏(不推荐)<br>

4. 设计目的

  • 抽象方法:用于定义必须由子类实现的契约,基类只规定方法签名,不提供默认行为。例如:
    public abstract class Shape
    {public abstract double Area(); // 所有形状必须计算面积
    }
    
  • 虚方法:用于提供可扩展的默认行为,允许子类在需要时修改实现。例如:
    public class Animal
    {public virtual void Speak() { Console.WriteLine("Animal sound"); }
    }public class Dog : Animal
    {public override void Speak() { Console.WriteLine("Woof"); } // 可选重写
    }
    

5. 调用方式

  • 抽象方法:无法直接调用,必须通过派生类的实现调用。
  • 虚方法:可以直接通过基类调用默认实现,也可以通过派生类调用重写后的实现。

总结对比表

特性抽象方法虚方法
方法体不能有方法体必须有默认实现
所在类必须在抽象类中可以在普通类或抽象类中
强制实现派生类必须实现派生类可选重写
关键字abstract + overridevirtual + override(可选)
设计意图定义必须实现的契约提供可扩展的默认行为

示例代码

// 抽象类 + 抽象方法
public abstract class Vehicle
{public abstract void Start(); // 必须由子类实现
}// 普通类 + 虚方法
public class Car : Vehicle
{public override void Start() { Console.WriteLine("Car started"); } // 实现抽象方法public virtual void Drive() { Console.WriteLine("Driving normally"); } // 虚方法,提供默认行为
}// 派生类重写虚方法
public class SportsCar : Car
{public override void Drive() { Console.WriteLine("Driving fast!"); } // 重写虚方法
}

何时使用?

  • 使用抽象方法:当基类无法提供有意义的默认实现,且所有子类必须强制实现某个行为时。
  • 使用虚方法:当基类可以提供默认行为,但子类可能需要自定义实现时。

通过合理使用抽象方法和虚方法,可以构建出灵活且易于扩展的面向对象系统。

2.2 虚方法和普通方法

在C#中,虚方法virtual)和普通方法(无修饰符)的核心区别在于是否支持运行时多态。以下是两者的详细对比:

1. 调用机制

虚方法普通方法
使用 virtual 关键字声明,支持运行时多态。基类的虚方法可以在派生类中被override重写。调用时,会根据对象的实际类型决定执行哪个版本的方法。没有特殊修饰符,不支持运行时多态。调用时,根据对象的声明类型决定执行的方法,无论对象的实际类型是什么。
示例
csharp<br>public class Animal {<br> public virtual void Speak() { Console.WriteLine("Animal"); }<br>}<br><br>public class Dog : Animal {<br> public override void Speak() { Console.WriteLine("Dog"); }<br>}<br><br>// 输出:Dog<br>Animal animal = new Dog();<br>animal.Speak(); // 调用Dog的实现<br>
示例
csharp<br>public class Animal {<br> public void Speak() { Console.WriteLine("Animal"); }<br>}<br><br>public class Dog : Animal {<br> public new void Speak() { Console.WriteLine("Dog"); } // 使用new隐藏基类方法(不推荐)<br>}<br><br>// 输出:Animal<br>Animal animal = new Dog();<br>animal.Speak(); // 调用Animal的实现<br>

2. 方法重写

虚方法普通方法
可以被派生类使用 override 关键字重写,从而改变方法的行为。不能被重写,但可以使用 new 关键字隐藏基类方法(但这不是真正的重写,只是创建了一个同名的新方法)。
正确做法
csharp<br>public class Base {<br> public virtual void Print() { ... }<br>}<br><br>public class Derived : Base {<br> public override void Print() { ... } // 重写虚方法<br>}<br>
错误做法(隐藏而非重写):
csharp<br>public class Base {<br> public void Print() { ... }<br>}<br><br>public class Derived : Base {<br> public new void Print() { ... } // 隐藏基类方法(编译警告)<br>}<br>

3. 设计意图

虚方法普通方法
用于实现多态性,允许基类定义通用行为,派生类根据需要自定义实现。例如:
csharp<br>public class Shape {<br> public virtual double Area() => 0;<br>}<br><br>public class Circle : Shape {<br> public override double Area() => Math.PI * Radius * Radius;<br>}<br>
用于实现固定行为,不希望派生类修改方法逻辑。例如:
csharp<br>public class Calculator {<br> public int Add(int a, int b) => a + b; // 不需要重写的方法<br>}<br>

4. 性能差异

  • 虚方法:调用时需要通过虚函数表(VTable)动态查找实际要执行的方法,因此性能略低(但在大多数场景下可以忽略不计)。
  • 普通方法:调用时直接绑定到声明类型的方法,性能更高

5. 语法对比表

特性虚方法普通方法
关键字virtual
能否重写能(使用 override不能(只能用 new 隐藏)
多态支持运行时多态(根据对象实际类型)编译时绑定(根据声明类型)
默认行为基类提供默认实现,可被覆盖行为固定,不可被派生类修改
性能略低(通过VTable查找)更高(直接调用)

总结:何时使用?

  • 使用虚方法
    • 当基类希望派生类能够自定义某个方法的实现时。
    • 需要通过基类引用调用派生类方法(实现多态)。
  • 使用普通方法
    • 当方法的逻辑不需要被派生类修改时。
    • 性能敏感的场景(如高频调用的方法)。

通过合理使用虚方法和普通方法,可以在保证代码灵活性的同时,避免不必要的性能开销。

三、迭代器的使用

3.1、使用场景及示例

在这里插入图片描述

在迭代块中,使用yield关键字选择要在foreach循环中使用的值,其语法如下

yield return <value>;

  • 迭代一个类成员(比如方法)IEnumerable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace SimpleIterators
{class Program{public static IEnumerable SimpleList(){yield return "string 1";yield return "string 2";yield return "string 3";}static void Main(string[] args){foreach (string item in SimpleList())Console.WriteLine(item);Console.ReadKey();}}
}

输出
在这里插入图片描述

  • 迭代一个类 Enumerator

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Ch11Ex03
{public class Primes{private long min;private long max;public Primes(): this(2, 100){}public Primes(long minimum, long maximum){if (minimum < 2)min = 2;elsemin = minimum;max = maximum;}public IEnumerator GetEnumerator(){for (long possiblePrime = min; possiblePrime <= max; possiblePrime++){bool isPrime = true;for (long possibleFactor = 2; possibleFactor <=(long)Math.Floor(Math.Sqrt(possiblePrime)); possibleFactor++){long remainderAfterDivision = possiblePrime % possibleFactor;if (remainderAfterDivision == 0){isPrime = false;break;}}if (isPrime){yield return possiblePrime;}}}}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Ch11Ex03
{class Program{static void Main(string[] args){Primes primesFrom2To1000 = new Primes(2, 1000);//Primes primesFrom2To1000 = new Primes( );foreach (long i in primesFrom2To1000)Console.Write("{0} ", i);Console.ReadKey();}}
}

primesFrom2To1000 为定义的一个类,在foreach里迭代这个类,
yield return possiblePrime;
为迭代要输出的值,输出的值的类型为定义的 long possiblePrime

输出如下
在这里插入图片描述

3.2、

四、深度复制与浅度复制

4.1、解析及示例

在C#中,深度复制(Deep Copy)浅度复制(Shallow Copy)是处理对象复制时的两种不同方式,它们的核心区别在于是否递归复制对象的所有成员。以下是详细解释和示例:

1. 浅度复制(Shallow Copy)

  • 定义:创建一个新对象,但只复制对象的顶层成员。对于引用类型的成员,只复制引用(内存地址),而不复制实际对象。
  • 特点
    • 新对象和原对象是不同的实例(内存地址不同)。
    • 引用类型的成员指向同一个对象。
    • 修改引用类型成员会影响所有关联的对象。
  • 实现方式
    • 使用 MemberwiseClone() 方法(受保护,需在类内部实现)。
    • 手动复制每个字段。

示例代码

public class Address
{public string City { get; set; }
}public class Person
{public string Name { get; set; } // 值类型public Address Address { get; set; } // 引用类型// 实现浅复制方法public Person ShallowCopy(){return (Person)this.MemberwiseClone();}
}// 使用示例
Person original = new Person
{Name = "张三",Address = new Address { City = "北京" }
};Person shallowCopy = original.ShallowCopy();// 修改浅复制对象的引用类型成员
shallowCopy.Address.City = "上海";Console.WriteLine(original.Address.City); // 输出: 上海(被修改了)

2. 深度复制(Deep Copy)

  • 定义:创建一个新对象,并递归复制对象的所有成员。对于引用类型的成员,会创建新的对象实例,而非仅复制引用。
  • 特点
    • 新对象和原对象完全独立,没有共享的引用类型成员。
    • 修改任何一个对象都不会影响其他对象。
  • 实现方式
    • 手动递归复制每个引用类型成员。
    • 使用序列化和反序列化(需类标记为 [Serializable])。

示例代码(手动实现)

public class Address
{public string City { get; set; }// 提供深度复制方法public Address DeepCopy(){return new Address { City = this.City };}
}public class Person
{public string Name { get; set; }public Address Address { get; set; }// 实现深度复制方法public Person DeepCopy(){return new Person{Name = this.Name,Address = this.Address.DeepCopy() // 递归复制引用类型};}
}// 使用示例
Person original = new Person
{Name = "张三",Address = new Address { City = "北京" }
};Person deepCopy = original.DeepCopy();// 修改深度复制对象的引用类型成员
deepCopy.Address.City = "上海";Console.WriteLine(original.Address.City); // 输出: 北京(未被修改)

示例代码(使用序列化)

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;[Serializable] // 必须标记为可序列化
public class Address
{public string City { get; set; }
}[Serializable]
public class Person
{public string Name { get; set; }public Address Address { get; set; }// 使用序列化实现深度复制public Person DeepCopy(){using (MemoryStream stream = new MemoryStream()){BinaryFormatter formatter = new BinaryFormatter();formatter.Serialize(stream, this);stream.Position = 0;return (Person)formatter.Deserialize(stream);}}
}
  1. 对比表格
    | 特性 | 浅度复制 | 深度复制 |
    |------------------------|------------------------------|------------------------------|
    | 新对象实例 | 创建顶层对象 | 创建所有层级的对象 |
    | 引用类型成员 | 共享同一个实例 | 创建新实例 |
    | 值类型成员 | 复制值 | 复制值 |
    | 实现复杂度 | 低(使用 MemberwiseClone) | 高(递归或序列化) |
    | 修改影响 | 影响所有共享引用的对象 | 仅影响当前对象 |
    | 性能 | 高(仅复制引用) | 低(需创建多个对象) |

  2. 常见问题

  • 循环引用:深度复制时需小心处理循环引用,可能导致栈溢出。
  • 不可序列化类型:使用序列化方法时,所有成员必须可序列化。
  • 性能开销:深度复制涉及创建多个对象,对性能有影响。
  1. 选择建议
  • 使用浅度复制:当对象的引用类型成员是不可变的,或不需要独立修改时。
  • 使用深度复制:当需要完全独立的对象,避免修改相互影响时。

总结

  • 浅度复制:复制顶层对象,共享引用类型成员。
  • 深度复制:递归复制所有成员,创建完全独立的对象。

理解这两种复制方式的区别,有助于避免在代码中出现意外的副作用,并根据需求选择合适的复制策略。

五、引用和值类型

在C#中,变量类型分为值类型引用类型,它们在内存存储、传递方式和生命周期等方面有本质区别。以下是常见的值类型和引用类型及其特点:

一、值类型(Value Types)

值类型变量直接存储数据值,通常分配在栈(Stack)上(局部变量)或结构体中。值类型的复制会创建独立的副本。

  1. 内置值类型
分类类型示例
整数byte, sbyte, short, ushort, int, uint, long, ulongint age = 30;
浮点数float, double, decimaldouble price = 9.99;
布尔boolbool isActive = true;
字符charchar letter = 'A';
枚举enum(自定义)enum Color { Red, Green, Blue };
元组(int, string)(C# 7.0+)var person = (1, "Alice");
  1. 结构体(Struct)
    结构体是用户自定义的值类型,常用于轻量级数据存储:
public struct Point
{public int X;public int Y;
}Point p1 = new Point { X = 10, Y = 20 };
Point p2 = p1; // 复制值,p2与p1独立
  1. 可空值类型(Nullable)
    允许值类型变量存储 null
int? nullableInt = null; // 可空整数
bool? nullableBool = false;

二、引用类型(Reference Types)

引用类型变量存储对象的内存地址(引用),对象本身分配在堆(Heap)上。引用类型的复制仅传递引用,多个变量可能指向同一对象。

  1. 内置引用类型
分类类型示例
字符串stringstring name = "John";
数组T[](任意类型的数组)int[] numbers = new int[5];
集合List<T>, Dictionary<TKey, TValue>, HashSet<T>List<string> names = new List<string>();
  1. 类(Class)
    类是最常见的引用类型,包括自定义类和框架类:
public class Person
{public string Name { get; set; }
}Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // 复制引用,p2和p1指向同一对象
  1. 接口(Interface)
    接口本身不能实例化,但实现接口的类是引用类型:
public interface IAnimal
{void Speak();
}public class Dog : IAnimal
{public void Speak() => Console.WriteLine("Woof!");
}IAnimal animal = new Dog(); // 引用类型
  1. 委托(Delegate)
    委托是方法的类型安全引用,属于引用类型:
public delegate void MyDelegate(string message);MyDelegate del = Console.WriteLine; // 委托实例
  1. 对象(Object)
    所有类型的基类,可引用任何类型的对象:
object obj = "Hello"; // 引用字符串对象
obj = 123; // 引用整数对象(装箱)
  1. 动态类型(Dynamic)
    在运行时确定类型,属于引用类型:
dynamic dynamicVar = "Hello";
dynamicVar = 123; // 运行时有效

三、关键区别总结

特性值类型引用类型
存储位置栈或结构体
复制方式创建独立副本复制引用(共享对象)
默认值0, false, null(可空类型)null
基类System.ValueTypeSystem.Object
常见类型基本数据类型、结构体、枚举类、接口、数组、字符串、委托

四、特殊注意事项

  1. 字符串的不可变性string 是引用类型,但由于不可变性,赋值时看似创建了副本:

    string a = "Hello";
    string b = a; // 复制引用,但字符串不可变
    b = "World";  // b指向新字符串,a不受影响
    
  2. 装箱与拆箱:值类型与 object 之间的转换会产生性能开销:

    int num = 100;
    object boxed = num; // 装箱(值类型→引用类型)
    int unboxed = (int)boxed; // 拆箱(引用类型→值类型)
    
  3. 结构体与类的选择

    • 结构体:轻量级、频繁创建/销毁、数据独立。
    • :复杂行为、需要继承、共享状态。

理解值类型和引用类型的区别是编写高效、安全C#代码的基础。根据场景选择合适的类型,可以避免内存泄漏、提高性能并减少错误。

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

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

相关文章

04 51单片机之数码管显示

文章目录1、前言2、数码管3、单个数码管引脚定义3-1、单个共阴极3-2、单个共阳极3-3、单个数码管引脚定义4、四位一体数码管引脚定义4-1、四位一体共阴极数码管4-2、四位一体共阳极数码管4-3、四位一体数码管引脚定义5、数码管原理图6、C51数组&#xff08;补充知识点&#xff…

【LLM】OpenRouter调用Anthropic Claude上下文缓存处理

背景 在使用OpenRouter调用Anthropic Claude大模型时&#xff0c;部分模型支持上下文缓存功能。当缓存命中时&#xff0c;调用成本会显著降低。虽然像DeepSeek这类模型自带上下文缓存机制&#xff0c;但本文主要针对构建Agent场景下&#xff0c;需要多次调用Anthropic Claude时…

【C++】第十七节—二叉搜索树(概念+性能分析+增删查+实现+使用场景)

好久不见&#xff0c;我是云边有个稻草人 《C》本文所属专栏—持续更新中—欢迎订阅 目录 一、二叉搜索树的概念 二、二叉搜索树的性能分析 三、二叉搜索树的插入 SearchBinaryTree.h test.cpp 四、⼆叉搜索树的查找 【只有一个3】 【有多个3】 五、⼆叉搜索树的删除…

Redis都有哪些数据结构,使用场景与原理解析

✅ String&#xff1a;字符串&#xff08;最常用、最简单的类型&#xff09;&#x1f4cc; 应用场景&#xff1a;计数器&#xff08;如&#xff1a;页面浏览量、点赞数、转发数等&#xff09;缓存单个值&#xff08;如&#xff1a;token、验证码、用户昵称&#xff09;分布式锁…

将EXCEL或者CSV转换为键值对形式的Markdown文件

# 创建命令行参数解析器parser argparse.ArgumentParser(description将 CSV 或 Excel 文件转换为带标头的 Markdown 格式)# 必需参数parser.add_argument(input_file, help输入文件路径 (CSV 或 Excel))parser.add_argument(output_file, help输出 Markdown 文件路径)# 可选参…

MySQL 配置性能优化实操指南:分版本5.7和8.0适配方案

在 MySQL 性能优化中&#xff0c;不同版本的特性差异会直接影响优化效果。本文基于 MySQL 5.7 和 8.0 两个主流版本&#xff0c;通过版本适配的配置代码、场景举例和通俗解释&#xff0c;让优化方案更精准落地。一、硬件与系统配置优化&#xff08;基础层优化&#xff09;1. 服…

【STM32实践篇】:串口通信

文章目录1. 串行通信与并行通信2. 异步通信与同步通信3. 单工&#xff0c;半双工和全双工通信4. 通信速率和接口标准5. USART 结构框图6. 串口电路6.1 串口之间的连接6.2 串口与 RS232 的转换和连接6.3 串口与 RS485 的转换和连接6.4 串口与 USB 的转换和连接7. USART 字符说明…

Trae IDE评测体验:通过 MCP Server - Figma AI Bridge 一键将 Figma 转为前端代码

Trae IDE评测体验&#xff1a;通过 MCP Server - Figma AI Bridge 一键将 Figma 转为前端代码 在现代前端开发中&#xff0c;从设计稿到可用页面的交付往往需要大量重复劳动&#xff1a;切图、手写样式、布局调整……而借助 MCP Server - Figma AI Bridge&#xff0c;我们可以…

文献阅读 250715-Atmospheric rivers cause warm winters and extreme heat events

Atmospheric rivers cause warm winters and extreme heat events 来自 <Atmospheric rivers cause warm winters and extreme heat events | Nature> ## Abstract: Definition: Atmospheric rivers (ARs) are narrow regions of intense water vapour transport in the …

线上协同办公时代:以开源AI大模型等工具培养网感,拥抱职业变革

摘要&#xff1a;在提倡线上协同办公的时代背景下&#xff0c;职场人需迅速提升工作能力以适应职业变革。培养网感成为时代所需&#xff0c;它为快速连接时代奠定基础。本文深入探讨了开源AI大模型、AI智能名片、S2B2C商城小程序源码等工具在培养网感过程中的重要作用&#xff…

Netty网络聊天室及扩展序列化算法

一、前言Netty是一个基于Java的高性能、事件驱动的网络应用框架&#xff0c;广泛应用于各种网络通信场景。本文将介绍如何使用Netty构建一个简单的网络聊天室&#xff0c;并扩展序列化算法来提高数据传输效率和灵活性。二、Netty网络聊天室的实现1. 项目结构我们将使用Maven构建…

基于单片机金沙河粮仓环境监测系统设计与实现

摘 要 本文围绕基于单片机的金沙河粮仓环境监测系统展开设计与实现研究。系统以单片机为核心&#xff0c;集成 DHT11、MQ - 135 等传感器&#xff0c;可实时精准监测粮仓温湿度、气体成分等关键环境参数。借助 LoRa、ESP8266 实现数据的可靠传输与远程通信 &#xff0c;OLED 屏…

如何解决Android Studio安装时无法下载SDK的问题(Windows、Linux、Mac解决方案大全)

如何解决Android Studio安装时无法下载SDK的问题&#xff08;Windows、Linux、Mac解决方案大全&#xff09; 前言 对于全栈开发者而言&#xff0c;安装 Android Studio 是迈向 Android 开发的第一步&#xff0c;但在 Windows、Linux、macOS 等不同平台上&#xff0c;经常会遇…

SQL Server从入门到项目实践(超值版)读书笔记 21

9.5 数据的内连接查询连接是关系数据库模型的主要特点&#xff0c;连接查询是关系数据库中最主要的查询&#xff0c;主要包括内连接、外连接等。内连接查询操作列出与连接条件匹配的数据行&#xff0c;它使用比较运算符比较被链接列的列值。具体语法格式如下&#xff1a;SELECT…

瑞芯微7月17日举办开发者大会,多款AIoT新品发布,触觉智能RK方案商报导

瑞芯微第九届开发者大会RKDC 2025将有多款新品发布。 据瑞芯微电子Rockchip此前宣布&#xff1a;该企业的本年度开发者大会RKDC 2025将于7月17~18日在福建福州海峡国际会展中心举行。本次瑞芯微开发者大会以“AIoT模型创新重做产品”为主题&#xff0c;关注传统IoT功能设备向场…

Eureka+Ribbon实现服务注册与发现

目录 一、相关文章 二、兼容说明 三、服务注册 四、服务发现 一、相关文章 基础工程&#xff1a;gradle7.6.1springboot3.2.4创建微服务工程-CSDN博客 Eureka服务端启动&#xff1a;https://blog.csdn.net/cherishSpring/article/details/149473554 Ribbon负载均衡&#…

数据库、HTML

一、数据库 数据库文件与普通文件区别: 普通文件对数据管理(增删改查)效率低2.数据库对数据管理效率高&#xff0c;使用方便 常用数据库: 1.关系型数据库: 将复杂的数据结构简化为二维表格形式 大型:0racle、DB2 中型:MySq1、sQLServer 小型:Sqlite 2.非关系型数据库以键值对…

RCE随笔(1)

哪些是可以执行代码执行&#xff1a;php代码。eval如&#xff1a;eval:<?php eval($_post[key]);eval&#xff1a;php中不被叫做函数叫动态执行命令assert&#xff1a;执行函数call_user_func_array<?php call_user_func_array(assert,array($_REQUEST[shu]));传入xxs-…

FPGA——ZYNQ7020学习日记(PS端)4(开始PS控制VGA显示)

1.DMA 我们的整体VGA显示分为几步&#xff1a;比如先导入VIDEO TIMING CONTROL来做对输入数据的时序“对齐”&#xff0c;这里开源骚客写的很详细&#xff0c;先用了一个虚拟IO&#xff08;VIO)来作为输入&#xff0c;导入了一个简单的RTL模块&#xff08;当VTL的使能信号有效…

AGX Xavier 搭建360环视教程【补充一:鱼眼去畸变(Fisheye Undistortion)】

对每路帧做鱼眼去畸变除了用cv::cuda::remap是否有更好的办法呢&#xff1f;确实 cv::cuda::remap 不是唯一可选项&#xff0c;甚至未必是最高效或最适合实际业务量级的方案。&#x1f3af; 1️⃣ 去畸变的原理鱼眼相机&#xff08;或者大广角相机&#xff09;会把直线拉弯&…