软件开发面试题C#,.NET知识点(续)
1.C#中的封装是什么,以及它的重要性。
封装(Encapsulation) 是面向对象编程(OOP)的一个基本概念。它指的是将对象的状态(属性)和行为(方法)绑定在一起,并且将这些细节隐藏起来,只暴露必要的接口给外部使用。这样做的好处包括:
- 提高代码的安全性:通过将数据隐藏在类内部,并通过公共方法(getters 和 setters)来访问和修改这些数据,可以防止外部代码直接修改类的内部状态,从而保护数据的完整性。
- 提高代码的可维护性:封装使得类的内部实现细节对外部代码透明,可以随时更改而不影响外部代码,只要接口保持不变。
- 增强代码的可读性和可理解性:通过合理的封装,可以将类的功能模块化,使代码结构更清晰,易于理解。
在C#中,封装通常通过使用访问修饰符(如 public
、private
、protected
)来实现。例如:
public class Person
{// 私有字段private string name;private int age;// 公共属性public string Name{get { return name; }set { name = value; }}public int Age{get { return age; }set {if (value >= 0){age = value;}}}// 构造函数public Person(string name, int age){this.name = name;this.Age = age; // 使用属性进行验证}
}
name
和 age
字段是私有的,外部代码无法直接访问,只能通过 Name
和 Age
属性进行访问和修改,从而实现了封装。
2.C#中的多态性是什么,以及它是如何实现的?
多态性(Polymorphism) 是面向对象编程中的另一个基本概念。它指的是不同对象可以用相同的接口进行操作,但表现出不同的行为。多态性使得同一操作可以作用于不同的对象,调用的方式一致,但实际的执行效果可以根据具体对象而有所不同。
在C#中,多态性主要通过以下两种方式实现:
-
方法重载(Overloading):在同一个类中,定义多个具有相同名称但参数不同的方法。调用哪个方法由传入的参数类型和数量决定。
public class Example {public void Display(int a){Console.WriteLine("Integer: " + a);}public void Display(string b){Console.WriteLine("String: " + b);} }
2.方法重写(Overriding):在继承关系中,子类可以重写父类的虚方法。调用哪个方法由实际对象的类型决定。
public class Animal {public virtual void Speak(){Console.WriteLine("Animal speaks");} }public class Dog : Animal {public override void Speak(){Console.WriteLine("Dog barks");} }public class Cat : Animal {public override void Speak(){Console.WriteLine("Cat meows");} }class Program {static void Main(string[] args){Animal myAnimal = new Dog();myAnimal.Speak(); // 输出:Dog barksmyAnimal = new Cat();myAnimal.Speak(); // 输出:Cat meows} }
Animal类有一个虚方法Speak,Dog类和Cat类分别重写了这个方法。当我们通过Animal类型的变量调用Speak方法时,实际调用的是具体对象类型的方法。
多态性的好处包括:
代码的灵活性和可扩展性:可以用统一的接口处理不同类型的对象,增加了代码的灵活性。
代码的可维护性:通过多态性,可以更容易地扩展和修改程序,而无需改变调用代码。
3.续接上一张Linq补充
-
选择操作(Select):投影操作,用于从数据源中选择指定的列。
int[] numbers = { 1, 2, 3, 4, 5 }; var squaredNumbers = numbers.Select(x => x * x); foreach (var num in squaredNumbers) {Console.WriteLine(num); // 输出:1, 4, 9, 16, 25 }
-
过滤操作(Where):筛选操作,用于从数据源中过滤出满足指定条件的元素。
int[] numbers = { 1, 2, 3, 4, 5 }; var evenNumbers = numbers.Where(x => x % 2 == 0); foreach (var num in evenNumbers) {Console.WriteLine(num); // 输出:2, 4 }
-
排序操作(OrderBy, OrderByDescending):对数据源进行升序或降序排序。
string[] names = { "Alice", "Bob", "Charlie" }; var sortedNames = names.OrderBy(name => name); foreach (var name in sortedNames) {Console.WriteLine(name); // 输出:Alice, Bob, Charlie }
-
分组操作(GroupBy):将数据源中的元素按指定键分组。
string[] names = { "Alice", "Bob", "Charlie", "Ann" }; var groupedNames = names.GroupBy(name => name[0]); foreach (var group in groupedNames) {Console.WriteLine(group.Key); // 输出:A, B, Cforeach (var name in group){Console.WriteLine(name); // 输出:Alice, Ann, Bob, Charlie} }
-
聚合操作(Sum, Count, Max, Min, Average):对数据源进行聚合计算。
int[] numbers = { 1, 2, 3, 4, 5 }; int sum = numbers.Sum(); int count = numbers.Count(); int max = numbers.Max(); int min = numbers.Min(); double average = numbers.Average();Console.WriteLine($"Sum: {sum}, Count: {count}, Max: {max}, Min: {min}, Average: {average}"); // 输出:Sum: 15, Count: 5, Max: 5, Min: 1, Average: 3
-
连接操作(Join):连接两个数据源,类似于SQL中的JOIN操作。
var students = new[] {new { StudentId = 1, Name = "Alice" },new { StudentId = 2, Name = "Bob" } };var scores = new[] {new { StudentId = 1, Score = 85 },new { StudentId = 2, Score = 90 } };var studentScores = students.Join(scores,student => student.StudentId,score => score.StudentId,(student, score) => new { student.Name, score.Score });foreach (var studentScore in studentScores) {Console.WriteLine($"{studentScore.Name}: {studentScore.Score}");// 输出:Alice: 85, Bob: 90 }
LINQ查询表达式语法
除了方法语法,LINQ还支持查询表达式语法:
int[] numbers = { 1, 2, 3, 4, 5 };
var evenNumbers = from num in numberswhere num % 2 == 0select num;
foreach (var num in evenNumbers)
{Console.WriteLine(num); // 输出:2, 4
}
4.垃圾回收机制
垃圾回收机制(Garbage Collection, GC) 是.NET框架的一项自动内存管理功能。它的作用是自动释放不再使用的对象所占用的内存,以避免内存泄漏和优化内存使用。
垃圾回收的工作原理
-
根(Root)对象:根对象是应用程序中直接引用的对象,例如静态变量、局部变量、CPU寄存器中的引用等。
-
可达对象(Reachable Object):从根对象出发,可以直接或间接引用到的对象。这些对象被认为是“存活”的,不会被垃圾回收。
-
不可达对象(Unreachable Object):无法从根对象到达的对象。这些对象被认为是“死亡”的,可以被垃圾回收。
垃圾回收的步骤
-
标记(Marking)阶段:GC从根对象开始遍历内存中的所有对象,标记所有可达对象。
-
清除(Sweeping)阶段:GC遍历内存,释放所有未被标记的对象所占用的内存。
-
压缩(Compacting)阶段(可选):GC将存活的对象移动到内存的连续区域,减少内存碎片,提高内存分配效率。
代(Generation)机制
.NET的垃圾回收机制采用代(Generation)机制将内存划分为不同的区域,以提高效率:
- 第0代(Generation 0):存放新分配的对象。垃圾回收频繁发生在第0代,因为大部分对象都是短期存在的。
- 第1代(Generation 1):存放从第0代提升的对象。这些对象通常存活时间稍长。
- 第2代(Generation 2):存放从第1代提升的对象。垃圾回收很少发生在第2代,因为这些对象通常是长期存在的。
垃圾回收的触发条件
垃圾回收器在以下情况下会被触发:
- 内存不足:当系统内存不足时,GC会触发以释放更多内存。
- 代满:当某一代的内存区域满时,GC会触发以回收内存。
- 显式调用:开发者可以通过调用
GC.Collect()
方法显式触发垃圾回收,但通常不建议这样做,除非有特殊需求。
示例代码
以下是一个简单示例,展示如何使用GC类来获取垃圾回收信息:
using System;public class Program
{public static void Main(string[] args){// 分配一些内存byte[] data = new byte[1024 * 1024 * 10]; // 分配10MB内存Console.WriteLine("Memory allocated.");// 显示GC信息Console.WriteLine("Generation of data: " + GC.GetGeneration(data));Console.WriteLine("Total memory: " + GC.GetTotalMemory(false) + " bytes");// 触发垃圾回收GC.Collect();GC.WaitForPendingFinalizers();// 显示GC信息Console.WriteLine("Total memory after GC: " + GC.GetTotalMemory(false) + " bytes");}
}
在这个示例中:
- 分配了10MB的内存,并显示了该对象所在的代。
- 通过调用
GC.Collect()
显式触发垃圾回收,并等待所有终结器执行完毕。 - 显示垃圾回收后的总内存。
垃圾回收的优点
- 自动内存管理:开发者无需手动释放内存,减少了内存泄漏的风险。
- 提高应用程序稳定性:GC自动处理内存管理,使应用程序更稳定。
- 优化内存使用:GC可以优化内存分配和回收,提高应用程序性能。
5.C#中的扩展方法(Extension Methods)是什么,它们的用途是什么?
扩展方法(Extension Methods) 是C#中的一种特殊静态方法,它允许你向现有类型添加新方法,而无需修改这些类型的源代码或创建新的派生类型。扩展方法在调用时看起来像是类型的实例方法,这使得它们非常方便。
扩展方法的定义和使用
-
定义扩展方法:扩展方法是一个静态方法,它必须定义在一个静态类中。第一个参数指定要扩展的类型,并且必须使用
this
关键字修饰。public static class StringExtensions {public static int WordCount(this string str){return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;} }
在这个例子中,
WordCount
方法是一个扩展方法,它扩展了string
类型,计算字符串中的单词数量。 -
使用扩展方法:扩展方法可以像实例方法一样调用。
public class Program {public static void Main(string[] args){string sentence = "Hello, how are you?";int count = sentence.WordCount();Console.WriteLine($"Word count: {count}"); // 输出:Word count: 4} }
扩展方法的用途
-
增强现有类型:可以向现有类型添加新的功能,而不需要继承或修改原始类型。例如,可以向
string
类型添加自定义的方法。 -
代码可读性和可维护性:扩展方法使代码更具可读性和可维护性。它们允许你在不改变类型定义的情况下添加新的行为。
-
LINQ:扩展方法是LINQ的基础。许多LINQ操作符(如
Select
、Where
等)都是通过扩展方法实现的。
示例:更多扩展方法
下面是一些示例,展示如何使用扩展方法:
-
扩展
int
类型:public static class IntExtensions {public static bool IsEven(this int number){return number % 2 == 0;} }
使用扩展方法:
public class Program {public static void Main(string[] args){int number = 4;bool isEven = number.IsEven();Console.WriteLine($"{number} is even: {isEven}"); // 输出:4 is even: True} }
-
扩展
List<T>
类型:public static class ListExtensions {public static void PrintAll<T>(this List<T> list){foreach (var item in list){Console.WriteLine(item);}} }
使用扩展方法:
public class Program {public static void Main(string[] args){List<string> names = new List<string> { "Alice", "Bob", "Charlie" };names.PrintAll(); // 输出:Alice, Bob, Charlie} }
注意事项
- 扩展方法不能访问被扩展类型的私有成员。
- 如果扩展方法与类型本身的方法或另一个扩展方法冲突,实例方法优先于扩展方法。
- 扩展方法的命名空间必须在使用时导入。
6.C#中的值类型(Value Types)和引用类型(Reference Types)的区别是什么?
在C#中,类型可以分为两大类:值类型(Value Types) 和 引用类型(Reference Types)。它们在内存分配和管理方面有很大的区别。
值类型(Value Types)
值类型直接包含其数据,分配在堆栈上。常见的值类型包括所有基本数据类型(如 int
、float
、char
)、struct
和 enum
。
特点:
- 存储位置:值类型存储在堆栈(stack)上。
- 数据访问:直接包含其数据。
- 赋值操作:值类型赋值时,会复制整个对象。两个变量各自独立,修改一个不会影响另一个。
- 内存管理:生命周期由包含它的作用域决定,超出作用域后会自动释放。
示例:
int a = 5;
int b = a; // b是a的副本,修改b不会影响a
b = 10;
Console.WriteLine(a); // 输出:5
Console.WriteLine(b); // 输出:10
引用类型(Reference Types)
引用类型存储对象的引用,而不是对象本身。对象实际数据存储在托管堆(heap)上,引用存储在堆栈上。常见的引用类型包括类(class
)、接口(interface
)、数组和委托(delegate
)。
特点:
- 存储位置:引用类型的实例存储在堆(heap)上,引用存储在堆栈(stack)上。
- 数据访问:包含对其数据的引用(即指针或地址)。
- 赋值操作:引用类型赋值时,复制的是对象的引用。两个变量指向同一个对象,修改一个会影响另一个。
- 内存管理:生命周期由垃圾回收机制(Garbage Collector, GC)管理,GC会自动回收不再使用的对象。
示例:
class Person
{public string Name { get; set; }
}Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // p2是对p1的引用,修改p2会影响p1
p2.Name = "Bob";
Console.WriteLine(p1.Name); // 输出:Bob
Console.WriteLine(p2.Name); // 输出:Bob
区别总结
- 内存分配:值类型分配在堆栈上,引用类型分配在堆上。
- 数据存储:值类型直接存储数据,引用类型存储数据的引用。
- 赋值操作:值类型赋值复制数据,引用类型赋值复制引用。
- 生命周期管理:值类型由作用域控制,引用类型由垃圾回收器管理。
值类型和引用类型的使用场景
- 值类型 适用于数据量小且生命周期短的场景,如数值运算、坐标点、颜色值等。
- 引用类型 适用于数据量大且结构复杂的场景,如对象关系、图形界面元素、数据库连接等。
7.C#中的封装(Encapsulation)是什么,以及它的优点是什么?
封装(Encapsulation) 是面向对象编程(OOP)的四大基本原则之一。它指的是将对象的状态(属性)和行为(方法)绑定在一起,并对其进行访问控制,即通过访问修饰符(如 public
、private
、protected
)来限制外部对对象内部数据的直接访问。
封装的目的
-
隐藏实现细节:通过封装,可以隐藏对象的内部实现细节,只暴露必要的接口给外部使用者。这样可以提高代码的安全性和可维护性。
-
保护数据完整性:通过控制对属性的访问,可以防止不合理的或未经授权的操作,从而保护数据的完整性和一致性。
-
提高代码复用性和灵活性:封装可以使代码模块化,每个类或对象负责自己的行为,从而提高代码的复用性和灵活性。
如何实现封装
在C#中,封装通常通过以下方式实现:
-
使用访问修饰符:控制属性和方法的访问级别。
public
:公开的,可以被任何代码访问。private
:私有的,只能被定义它的类内部访问。protected
:受保护的,可以被定义它的类和子类访问。internal
:内部的,只能在同一程序集内访问。protected internal
:受保护的内部,可以在同一程序集内或子类中访问。
-
使用属性(Properties):通过属性来控制对私有字段的访问。属性可以定义
get
和set
访问器,提供对字段的读写操作。
示例
以下是一个示例,展示如何通过封装来保护数据并提供对外接口:
public class Person
{// 私有字段,外部无法直接访问private string name;private int age;// 公有属性,通过属性访问器控制对私有字段的访问public string Name{get { return name; }set{if (!string.IsNullOrEmpty(value)){name = value;}else{throw new ArgumentException("Name cannot be null or empty.");}}}public int Age{get { return age; }set{if (value >= 0 && value <= 120){age = value;}else{throw new ArgumentException("Age must be between 0 and 120.");}}}// 公有方法,提供对外的行为接口public void DisplayInfo(){Console.WriteLine($"Name: {Name}, Age: {Age}");}
}
使用该类:
public class Program
{public static void Main(string[] args){Person person = new Person();// 通过属性访问器设置和获取值person.Name = "Alice";person.Age = 30;person.DisplayInfo(); // 输出:Name: Alice, Age: 30// 尝试设置无效值会抛出异常try{person.Age = -5;}catch (ArgumentException ex){Console.WriteLine(ex.Message); // 输出:Age must be between 0 and 120.}}
}
封装的优点
提高安全性:通过控制对属性和方法的访问,可以防止外部代码对对象内部数据的非法访问和修改,从而提高安全性。
增强可维护性:封装使对象的内部实现细节对外部透明,修改对象的内部实现不会影响外部代码,从而提高代码的可维护性。
代码复用性:通过封装,可以将相关的属性和方法组织在一起,提高代码的复用性。
模块化设计:封装支持模块化设计,使代码结构更清晰,每个类或对象负责自己的行为,从而提高代码的可读性和可扩展性。
8. C#中的多态性(Polymorphism)是什么,以及它的优点是什么?
多态性(Polymorphism) 是面向对象编程(OOP)的四大基本原则之一。它允许你通过相同的接口来调用不同的底层实现,从而使代码更具灵活性和可扩展性。在C#中,多态性主要通过方法重载(Overloading)和方法重写(Overriding)来实现。
方法重载(Overloading)
方法重载是指在同一个类中,可以定义多个具有相同名称但参数列表不同的方法。编译器根据方法的参数列表来确定调用哪个具体的方法。
示例:
public class MathOperations
{public int Add(int a, int b){return a + b;}public double Add(double a, double b){return a + b;}
}
在这个例子中,Add
方法被重载了两次,一个接收 int
参数,另一个接收 double
参数。根据传递的参数类型,编译器会选择调用相应的方法。
方法重写(Overriding)
方法重写是指在子类中重新定义基类中定义的方法。为了实现方法重写,基类的方法必须标记为 virtual
或 abstract
,而子类的方法必须使用 override
关键字。
示例:
public class Animal
{public virtual void MakeSound(){Console.WriteLine("The animal makes a sound.");}
}public class Dog : Animal
{public override void MakeSound(){Console.WriteLine("The dog barks.");}
}public class Cat : Animal
{public override void MakeSound(){Console.WriteLine("The cat meows.");}
}
在这个例子中,基类 Animal
定义了一个虚方法 MakeSound
。子类 Dog
和 Cat
重写了这个方法,提供了各自的实现。
使用多态性
通过多态性,可以使用基类引用来指向子类对象,并调用子类重写的方法。
示例:
public class Program
{public static void Main(string[] args){Animal myDog = new Dog();Animal myCat = new Cat();myDog.MakeSound(); // 输出:The dog barks.myCat.MakeSound(); // 输出:The cat meows.}
}
在这个例子中,myDog
和 myCat
都是 Animal
类型的引用,但它们实际指向 Dog
和 Cat
对象。调用 MakeSound
方法时,会根据实际对象的类型调用相应的方法实现。
多态性的优点
提高代码的灵活性和可扩展性:通过多态性,可以使用相同的接口来调用不同的实现,方便在不修改现有代码的情况下扩展新功能。
简化代码维护:多态性使得代码更加模块化,每个类负责自己的行为,从而提高代码的可维护性。
支持动态绑定:在运行时,根据对象的实际类型选择合适的方法实现,从而提供更灵活的代码执行。
9. C#中的接口(Interface)是什么,以及它们的用途是什么?(详细版)
接口(Interface) 是C#中的一种类型,它定义了一组方法、属性、事件或索引器,而不包含这些成员的实现。接口只提供这些成员的签名(方法名、参数列表和返回类型),具体的实现由实现接口的类或结构体来完成。
接口的定义和实现
定义接口
接口使用 interface
关键字来定义。接口中的成员默认是 public
的,并且不允许包含访问修饰符。
public interface IAnimal
{void MakeSound();void Move();
}
在这个例子中,接口 IAnimal
定义了两个方法 MakeSound
和 Move
,但没有提供任何实现。
实现接口
类或结构体通过使用 :
符号来实现接口,并提供接口中定义的所有成员的具体实现。
public class Dog : IAnimal
{public void MakeSound(){Console.WriteLine("The dog barks.");}public void Move(){Console.WriteLine("The dog runs.");}
}public class Cat : IAnimal
{public void MakeSound(){Console.WriteLine("The cat meows.");}public void Move(){Console.WriteLine("The cat walks.");}
}
在这个例子中,Dog
和 Cat
类都实现了 IAnimal
接口,并提供了 MakeSound
和 Move
方法的具体实现。
接口的用途
-
定义协议:接口定义了一组方法和属性,这些方法和属性代表了一组通用的行为。任何类或结构体只要实现了这个接口,就必须提供这些行为的具体实现。
-
实现多态性:通过接口,可以实现多态性。你可以使用接口类型的变量来引用实现该接口的任何对象,从而实现方法调用的动态绑定。
-
松耦合设计:接口提供了一种松耦合的设计方式,使得系统中的各个部分可以独立变化和扩展。只要类实现了相应的接口,就可以替换为新的实现,而不会影响依赖该接口的代码。
-
代码复用:通过接口,可以实现代码的复用。不同的类可以实现同一个接口,从而共享接口定义的行为。
示例:接口的使用
以下是一个示例,展示如何使用接口来实现多态性和松耦合设计:
public interface IAnimal
{void MakeSound();
}public class Dog : IAnimal
{public void MakeSound(){Console.WriteLine("The dog barks.");}
}public class Cat : IAnimal
{public void MakeSound(){Console.WriteLine("The cat meows.");}
}public class AnimalShelter
{private List<IAnimal> animals = new List<IAnimal>();public void AddAnimal(IAnimal animal){animals.Add(animal);}public void MakeAllAnimalsSound(){foreach (var animal in animals){animal.MakeSound();}}
}public class Program
{public static void Main(string[] args){AnimalShelter shelter = new AnimalShelter();IAnimal dog = new Dog();IAnimal cat = new Cat();shelter.AddAnimal(dog);shelter.AddAnimal(cat);shelter.MakeAllAnimalsSound();// 输出:// The dog barks.// The cat meows.}
}
在这个示例中:
IAnimal
接口定义了MakeSound
方法。Dog
和Cat
类实现了IAnimal
接口。AnimalShelter
类使用IAnimal
类型的列表来存储动物,并通过接口调用MakeSound
方法。- 通过
AnimalShelter
类,可以向动物收容所添加不同类型的动物,并调用它们的MakeSound
方法,而无需关心具体的动物类型。
总结
接口是C#中定义行为协议的重要工具。通过接口,可以实现多态性、松耦合设计和代码复用,从而使代码更加灵活、可扩展和易于维护。
10.C#中的事件(Events)是什么,以及它们的用途是什么?
事件(Events) 是C#中的一种特殊成员,它允许类或对象通过事件通知其他类或对象某些事情发生了。事件通常用于实现发布-订阅模式,这是一种常见的设计模式,用于在对象之间进行通信。
事件的定义和使用
事件的定义
事件通过 event
关键字来定义。通常,事件是基于委托(delegate)的。委托定义了事件处理程序的签名,事件则使用这个委托来管理其订阅者。
public delegate void NotifyEventHandler(object sender, EventArgs e);public class Publisher
{public event NotifyEventHandler Notify;public void RaiseEvent(){if (Notify != null){Notify(this, EventArgs.Empty);}}
}
在这个例子中,NotifyEventHandler
是一个委托,它定义了事件处理程序的方法签名。Publisher
类定义了一个 Notify
事件,并在 RaiseEvent
方法中触发该事件。
事件的订阅和处理
要处理事件,需要订阅该事件,并提供一个与委托签名匹配的方法。
public class Subscriber
{public void OnNotify(object sender, EventArgs e){Console.WriteLine("Subscriber received the event.");}
}public class Program
{public static void Main(string[] args){Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();// 订阅事件publisher.Notify += subscriber.OnNotify;// 触发事件publisher.RaiseEvent();}
}
在这个例子中,Subscriber
类定义了一个事件处理方法 OnNotify
,并订阅了 Publisher
类的 Notify
事件。当 Publisher
触发 Notify
事件时,Subscriber
的 OnNotify
方法会被调用。
事件的用途
-
解耦:事件提供了一种松耦合的方式来实现对象之间的通信。发布者无需知道订阅者的存在,只需触发事件,订阅者会自动收到通知。
-
异步处理:事件允许异步处理某些操作。例如,当用户点击按钮时,可以触发点击事件,事件处理程序可以在后台执行。
-
扩展性:通过事件,可以轻松地扩展系统功能,而无需修改现有代码。只需添加新的事件处理程序即可。
示例:更多事件的使用
以下是一个更复杂的示例,展示如何使用事件来处理按钮点击事件:
public class Subscriber
{public void OnNotify(object sender, EventArgs e){Console.WriteLine("Subscriber received the event.");}
}public class Program
{public static void Main(string[] args){Publisher publisher = new Publisher();Subscriber subscriber = new Subscriber();// 订阅事件publisher.Notify += subscriber.OnNotify;// 触发事件publisher.RaiseEvent();}
}
在这个例子中,Button
类定义了一个 Click
事件,当按钮被点击时,触发该事件。ButtonHandler
类定义了一个事件处理程序 Button_Click
,订阅了 Button
的 Click
事件。当 Button
触发 Click
事件时,ButtonHandler
的 Button_Click
方法会被调用。
总结
事件是C#中实现发布-订阅模式的重要机制。通过事件,可以实现对象之间的松耦合通信,异步处理操作,并提高系统的扩展性。理解和掌握事件的使用对于编写灵活、可扩展的代码非常重要。
11. 依赖注入(Dependency Injection, DI)是什么,它的优点是什么?并简要介绍如何在C#中使用依赖注入。
依赖注入(Dependency Injection,DI) 是一种设计模式,用于实现控制反转(Inversion of Control, IoC)。它通过将依赖关系注入到类的构造函数、属性或方法中,使得类与其依赖项解耦,从而提高代码的可测试性、可维护性和可扩展性。
依赖注入的概念
在传统编程中,类通常直接创建其依赖对象,这导致类与依赖项紧密耦合。依赖注入通过外部提供依赖对象,避免了类直接创建依赖项,从而实现了松耦合。
示例:没有依赖注入
public class Service
{public void Execute(){Console.WriteLine("Service executed.");}
}public class Client
{private Service _service;public Client(){_service = new Service();}public void Start(){_service.Execute();}
}
在这个例子中,Client
类直接创建了 Service
类的实例,因此它们之间是紧耦合的。
示例:使用依赖注入
public class Service
{public void Execute(){Console.WriteLine("Service executed.");}
}public class Client
{private Service _service;public Client(Service service){_service = service;}public void Start(){_service.Execute();}
}
在这个例子中,Client
类通过构造函数接收 Service
的实例,从而实现了依赖注入。
依赖注入的优点
-
提高代码的可测试性:通过依赖注入,可以轻松地替换依赖项,从而进行单元测试。例如,可以使用模拟对象(mock objects)来替换实际的依赖项。
-
增强代码的可维护性和可扩展性:依赖注入使得类与其依赖项解耦,从而更容易修改和扩展代码。例如,可以在不修改类的情况下替换或添加新的依赖项。
-
遵循SOLID原则:依赖注入有助于遵循SOLID设计原则,特别是单一职责原则(SRP)和依赖倒置原则(DIP)。
在C#中使用依赖注入
C#中有多种依赖注入框架,例如Unity、Ninject和Autofac。在.NET Core和.NET 5+中,依赖注入已成为框架的一部分。以下是一个使用.NET Core内置依赖注入的示例:
示例:使用.NET Core内置依赖注入
- 定义接口和实现类
public interface IService {void Execute(); }public class Service : IService {public void Execute(){Console.WriteLine("Service executed.");} }public class Client {private readonly IService _service;public Client(IService service){_service = service;}public void Start(){_service.Execute();} }
- 配置依赖注入容器
using Microsoft.Extensions.DependencyInjection; using System;public class Program {public static void Main(string[] args){// 创建服务容器var serviceProvider = new ServiceCollection().AddSingleton<IService, Service>().AddSingleton<Client>().BuildServiceProvider();// 获取Client实例并调用其方法var client = serviceProvider.GetService<Client>();client.Start();} }
在这个示例中:
IService
接口和Service
实现类定义了服务。Client
类依赖于IService
接口,通过构造函数注入实现依赖注入。- 在
Main
方法中,使用ServiceCollection
配置依赖注入容器,并注册服务。 - 使用
serviceProvider.GetService<Client>()
获取Client
实例并调用其方法。
总结
依赖注入是一种设计模式,通过将依赖项注入到类中,实现类与其依赖项的解耦。它提高了代码的可测试性、可维护性和可扩展性。在C#中,可以使用多种依赖注入框架或.NET Core内置的依赖注入容器来实现依赖注入。
12.C#中的属性(Properties)是什么,它们的作用是什么?
在C#中,属性(Properties)是一种特殊的成员,用于封装类的字段(fields),提供对字段的访问和修改。属性允许你定义一种访问和修改类成员的方式,同时可以隐藏具体的数据存储细节。属性通常用于提供对类的字段的安全访问,可以执行输入验证、计算值或与其他类交互。
属性的定义和用途
定义属性
在C#中,属性通常由两部分组成:get
访问器(getter)和 set
访问器(setter)。get
访问器用于获取属性的值,set
访问器用于设置属性的值。以下是一个简单的示例:
public class Person
{private string name; // 私有字段// 公共属性public string Name{get { return name; } // get 访问器用于获取值set { name = value; } // set 访问器用于设置值}
}
在这个例子中,Person
类有一个私有字段 name
和一个公共属性 Name
。通过属性 Name
,可以控制对 name
字段的访问。
使用属性
使用属性时,可以像访问字段一样使用点(.
)运算符:
Person person = new Person();
person.Name = "Alice"; // 设置属性值
string name = person.Name; // 获取属性值
在这个例子中,person.Name
实际上调用了 Name
属性的 set
和 get
访问器。
属性的优点
-
封装数据:属性允许将字段隐藏在类的内部,并通过公共接口提供对字段的访问。这样可以控制对数据的访问方式,从而提高代码的安全性和可维护性。
-
验证输入:属性可以在设置值时执行输入验证。例如,可以检查值是否满足特定条件(如范围检查或格式验证),从而确保数据的有效性。
-
计算值:属性可以根据特定的算法或逻辑来计算其值,而不是简单地返回字段的值。这在需要动态计算值或实时更新的情况下特别有用。
-
与其他类交互:属性可以通过与其他类交互来获取或设置其值。例如,属性可以调用其他类的方法来获取数据或更新数据。
示例:属性的使用
以下是一个示例,展示如何在C#中使用属性:
public class Circle
{private double radius;// 半径属性public double Radius{get { return radius; }set{if (value > 0)radius = value;elsethrow new ArgumentException("Radius must be positive.");}}// 计算圆的面积的只读属性public double Area{get { return Math.PI * radius * radius; }}
}public class Program
{public static void Main(){Circle circle = new Circle();circle.Radius = 5.0; // 设置半径属性Console.WriteLine($"Radius: {circle.Radius}");Console.WriteLine($"Area: {circle.Area}");}
}
在这个示例中:
Circle
类有一个私有字段radius
和两个属性Radius
和Area
。Radius
属性包含了输入验证,确保半径值大于0。Area
属性是一个只读属性,根据半径计算圆的面积。- 在
Main
方法中,演示了如何使用这些属性来设置和获取数据。
总结
属性是C#中一种重要的语言特性,用于封装类的字段并提供安全的访问和修改方法。通过属性,可以实现数据封装、输入验证、计算值和与其他类的交互。掌握属性的使用可以提高代码的可维护性和可扩展性,同时提升程序的安全性和灵活性。
13.C#中的集合(Collections)是什么,以及它们的作用是什么?
在C#中,集合(Collections)是用于存储和操作一组对象的数据结构。集合提供了比数组更灵活和功能更强大的方式来管理数据。使用集合,可以动态地添加、删除和修改元素,而无需关心底层数据结构和内存管理。
常见的集合类型
C#中提供了多种集合类型,每种类型都适用于不同的场景和需求:
-
List<T>:动态数组,允许快速地添加、删除和访问元素。通常用于需要频繁修改的情况。
-
Dictionary<TKey, TValue>:键值对集合,用于存储一对一的关系。通过键快速查找和访问值。
-
HashSet<T>:无重复元素的集合,用于存储唯一值,支持高效的集合运算(交集、并集、差集等)。
-
Queue<T>:先进先出(FIFO)的队列,用于顺序存储和访问元素。
-
Stack<T>:后进先出(LIFO)的栈,用于反向存储和访问元素。
-
LinkedList<T>:双向链表,允许高效地插入、删除和移动元素。
示例:使用集合
以下是一些示例,展示如何在C#中使用集合:
使用 List<T>
using System;
using System.Collections.Generic;public class Program
{public static void Main(){List<int> numbers = new List<int>();// 添加元素numbers.Add(1);numbers.Add(2);numbers.Add(3);// 遍历元素foreach (var number in numbers){Console.WriteLine(number);}}
}
使用 Dictionary<TKey, TValue>
using System;
using System.Collections.Generic;public class Program
{public static void Main(){Dictionary<string, int> ages = new Dictionary<string, int>();// 添加键值对ages["Alice"] = 30;ages["Bob"] = 25;ages["Charlie"] = 35;// 访问值Console.WriteLine($"Bob's age is {ages["Bob"]}");// 遍历键值对foreach (var person in ages){Console.WriteLine($"{person.Key}: {person.Value} years old");}}
}
集合的作用
使用集合可以帮助我们:
-
管理和操作数据:集合提供了丰富的方法和属性来管理数据,包括添加、删除、查找、排序等操作。
-
提高性能:集合类型底层实现了高效的数据结构,能够在不同操作场景下提供快速的执行速度。
-
增强代码的可读性和可维护性:使用集合能够更清晰地表达和操作数据,使代码更易于理解和维护。
总结
集合是C#中非常重要的一部分,它们提供了灵活、高效和易于使用的数据管理机制。不同类型的集合适用于不同的需求,选择合适的集合类型可以显著提升代码的性能和可维护性。
14.C#中的异步和多线程的区别是什么?
异步和多线程的区别
在C#中,异步编程和多线程都是用于提高程序性能和响应性的技术,但它们之间有一些重要的区别:
异步(Asynchronous)
异步编程允许程序在等待某些操作完成时不阻塞主线程,从而使得程序可以继续执行其他任务。异步操作通常用于处理I/O密集型操作,如文件读写、网络请求或数据库访问,这些操作可能会花费较长时间而主线程可以在等待时执行其他任务。
在C#中,异步操作通常使用 async
和 await
关键字来实现。通过 await
关键字可以等待异步操作的完成,而不会阻塞当前线程。
多线程(Multithreading)
多线程是同时执行多个线程的一种技术。每个线程都是程序的独立执行流,可以并行执行不同的任务。多线程通常用于处理CPU密集型任务,如大量计算或需要实时处理的任务。
在C#中,多线程可以通过 Thread
类或使用线程池(ThreadPool)来实现。使用多线程时,程序可以利用多核处理器的能力来提高性能,但需要注意线程间的同步和资源共享问题。
区别总结
-
执行方式:
- 异步:主要用于处理I/O操作,允许程序在等待期间执行其他任务,不阻塞主线程。
- 多线程:用于并行执行多个任务,可以利用多核处理器提高性能,适合CPU密集型任务。
-
适用场景:
- 异步:适用于等待外部操作完成的场景,如网络请求、文件读写。
- 多线程:适用于需要同时执行多个任务且任务之间相对独立的场景,如大量计算或并发处理任务。
-
实现方式:
- 异步:使用
async
和await
关键字实现异步操作。 - 多线程:使用
Thread
类、线程池或任务并行库(如Task
类)实现多线程。
- 异步:使用
示例
异步示例
using System;
using System.Net.Http;
using System.Threading.Tasks;public class Program
{public static async Task Main(){await FetchDataAsync(); // 异步方法调用Console.WriteLine("Main method continues...");}public static async Task FetchDataAsync(){HttpClient client = new HttpClient();string result = await client.GetStringAsync("https://jsonplaceholder.typicode.com/posts/1");Console.WriteLine(result);}
}
多线程示例
using System;
using System.Threading;public class Program
{public static void Main(){Thread thread1 = new Thread(DoWork);Thread thread2 = new Thread(DoWork);thread1.Start(); // 启动线程1thread2.Start(); // 启动线程2Console.WriteLine("Main method continues...");}public static void DoWork(){Console.WriteLine("Thread is working...");Thread.Sleep(2000); // 模拟耗时操作Console.WriteLine("Thread finished work.");}
}
在以上示例中:
-
异步示例中的
FetchDataAsync
方法使用了async
和await
实现异步操作,允许Main
方法在等待网络请求完成时继续执行。 -
多线程示例中的
DoWork
方法在不同的线程中执行,并且可以并行运行,每个线程独立处理任务。
总结
异步和多线程都是用于提高程序性能和响应性的重要技术,但它们适用于不同类型的任务和场景。理解它们的区别和适用情况可以帮助我们更有效地设计和实现多线程或异步的程序。
15.C#中的托管代码(Managed Code)和非托管代码(Unmanaged Code)的区别是什么?
托管代码(Managed Code)和非托管代码(Unmanaged Code)的区别
在C#和.NET平台中,代码可以分为托管代码和非托管代码,它们有以下主要区别:
托管代码(Managed Code)
-
定义:托管代码是由.NET运行时(CLR,Common Language Runtime)管理和执行的代码。编写在C#、VB.NET、F#等.NET语言中的代码都属于托管代码。
-
特点:
- 受到CLR的管理和控制,CLR负责内存管理、安全性检查、异常处理等。
- 通过垃圾回收(Garbage Collection,GC)来管理和释放不再使用的对象的内存。
- 具有跨平台性,可以在任何支持CLR的操作系统上运行。
-
优点:
- 简化了开发过程,提高了开发效率。
- 提供了内置的安全性和异常处理机制,减少了潜在的内存泄漏和访问越界问题。
非托管代码(Unmanaged Code)
-
定义:非托管代码是直接在操作系统上执行,并且不受CLR管理的代码。通常是使用C、C++等语言编写的代码。
-
特点:
- 不受CLR的管理和控制,需要手动管理内存、安全性和异常处理。
- 操作系统或硬件相关,对平台依赖性强。
-
优点:
- 可以直接访问和操作系统API和硬件,性能更高。
- 对于一些需要精细控制和特定平台优化的场景,非托管代码可能更适合。
示例
托管代码示例(C#)
using System;public class Program
{public static void Main(){// 托管代码示例string message = "Hello, managed world!";Console.WriteLine(message);}
}
非托管代码示例(C++)
#include <iostream>int main() {// 非托管代码示例std::cout << "Hello, unmanaged world!" << std::endl;return 0;
}
在以上示例中:
- 托管代码使用C#编写,由CLR管理和执行,可以在任何支持CLR的平台上运行。
- 非托管代码使用C++编写,直接在操作系统上执行,与特定平台相关。
总结
托管代码和非托管代码是在C#和.NET平台中的重要概念。托管代码由CLR管理和执行,提供了高级的开发功能和跨平台能力,而非托管代码则直接在操作系统上执行,更加接近硬件和操作系统,提供了更高的性能和灵活性,但也需要开发者手动管理内存和安全性。
16.C#中的索引器(Indexers)是什么,以及它们的作用是什么?
索引器(Indexers)的概念和作用
在C#中,索引器(Indexers)允许类的实例像数组一样通过索引访问对象的元素。它们提供了一种类似于属性的访问方式,但是使用方括号 []
运算符来定义和访问元素,而不是点号 .
运算符。
索引器的定义和用法
定义索引器
索引器类似于属性,但有一些不同之处。它们用 this
关键字定义,并且可以有一个或多个参数,参数用于指定要访问的元素的索引。索引器可以是只读的(没有 set
访问器)或读写的(同时有 get
和 set
访问器)。
下面是一个简单的示例:
public class MyCollection
{private string[] data = new string[10];// 索引器public string this[int index]{get{if (index >= 0 && index < data.Length)return data[index];elsethrow new IndexOutOfRangeException();}set{if (index >= 0 && index < data.Length)data[index] = value;elsethrow new IndexOutOfRangeException();}}
}
在这个例子中,MyCollection
类定义了一个索引器 this[int index]
,允许通过整数索引访问 data
数组的元素。get
访问器用于获取指定索引处的值,set
访问器用于设置指定索引处的值。
使用索引器
使用索引器时,可以像访问数组元素一样使用方括号运算符 []
:
MyCollection collection = new MyCollection();
collection[0] = "Item 1"; // 设置索引0处的值
string item = collection[0]; // 获取索引0处的值
Console.WriteLine(item); // 输出: Item 1
在这个示例中,collection[0] = "Item 1"
使用索引器的 set
访问器来设置索引0处的值,而 collection[0]
使用索引器的 get
访问器来获取索引0处的值。
索引器的作用
索引器提供了一种更灵活和方便的方式来访问类中的元素集合,特别是当类表示集合或列表时。通过使用索引器,可以使类的实例可以像数组或列表一样使用,同时还可以实现对元素的更复杂的访问逻辑。
总结
索引器是C#中一种重要的语言特性,允许类的实例通过索引访问其内部的元素集合。使用索引器,可以提供类似于数组访问的便利性和灵活性,同时允许定义复杂的访问逻辑。掌握索引器的使用可以提高代码的可读性和可维护性。
17.C#中的构造函数(Constructor)是什么,以及它们的作用是什么?
构造函数(Constructor)的概念和作用
在C#中,构造函数是一种特殊的方法,用于在创建类的实例时初始化对象的状态。每次创建类的实例时,都会调用构造函数来执行初始化操作。构造函数的名称与类名相同,并且没有返回类型(包括 void
),可以具有不同的参数列表,用于接受初始化时所需的参数。
构造函数的定义和用法
定义构造函数
构造函数在类中定义如下:
public class MyClass
{// 默认构造函数public MyClass(){// 初始化代码}// 带参数的构造函数public MyClass(string name, int age){// 使用参数进行初始化this.Name = name;this.Age = age;}// 属性public string Name { get; set; }public int Age { get; set; }
}
在上面的例子中:
MyClass
类定义了两个构造函数:- 默认构造函数
public MyClass()
:当没有参数传递给构造函数时调用,用于执行基本的初始化操作。 - 带参数的构造函数
public MyClass(string name, int age)
:接受name
和age
作为参数,用于根据传入的值初始化Name
和Age
属性。
- 默认构造函数
使用构造函数
创建类的实例时,可以根据不同的构造函数进行调用:
MyClass obj1 = new MyClass(); // 调用默认构造函数
MyClass obj2 = new MyClass("John", 30); // 调用带参数的构造函数
在这个示例中,obj1
和 obj2
分别使用了不同的构造函数来创建 MyClass
类的实例。通过构造函数,可以根据需要初始化对象的属性和状态。
构造函数的作用
构造函数的主要作用包括:
- 初始化对象的状态:设置对象的初始值,确保对象在创建后处于有效的状态。
- 接受参数:允许根据传入的参数进行不同的初始化操作,增加了灵活性。
- 执行必要的初始化逻辑:例如分配内存、打开资源等操作。
总结
构造函数是C#中用于初始化类实例的特殊方法,每次创建类的实例时都会调用适当的构造函数。通过构造函数,可以初始化对象的状态,并根据需要接受不同的参数进行初始化操作,从而提高代码的灵活性和可维护性。
相关文章:
软件开发面试题C#,.NET知识点(续)
1.C#中的封装是什么,以及它的重要性。 封装(Encapsulation) 是面向对象编程(OOP)的一个基本概念。它指的是将对象的状态(属性)和行为(方法)绑定在一起,并且将…...
2019年美赛题目Problem A: Game of Ecology
本题分析: 本题想要要求从实际生物角度出发,对权力游戏中龙这种虚拟生物的生态环境和生物特性进行建模,感觉属于比较开放类型的题目,重点在于参考生物的选择,龙虽然是虚拟的但是龙的生态特性可以参考目前生物圈里存在…...
沙龙回顾|MongoDB如何充当企业开发加速器?
数据不仅是企业发展转型的驱动力,也是开发者最棘手的问题。前日,MongoDB携手阿里云、NineData在杭州成功举办了“数据驱动,敏捷前行——MongoDB企业开发加速器”技术沙龙。此次活动吸引了来自各行各业的专业人员,共同探讨MongoDB的…...
云端编码:将您的技术API文档安全存储在iCloud的最佳实践
云端编码:将您的技术API文档安全存储在iCloud的最佳实践 作为一名技术专业人士,管理不断增长的API文档库是一项挑战。iCloud提供了一个无缝的解决方案,允许您在所有设备上存储、同步和访问您的个人技术API文档。本文将指导您如何在iCloud中高…...
在Spring Boot项目中集成单点登录解决方案
在Spring Boot项目中集成单点登录解决方案 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在现代的企业应用中,单点登录(Single Sign-On, SSO)解决方案是确保用户…...
Java-常用API
1-Java API : 指的就是 JDK 中提供的各种功能的 Java类。 2-Scanner基本使用 Scanner: 一个简单的文本扫描程序,可以获取基本类型数据和字符串数据 构造方法: Scanner(InputStream source):创建 Scanner 对象 Sy…...
Python从Excel表中查找指定数据填入新表
#读取xls文件中的数据 import xlrd file "原表.xls" wb xlrd.open_workbook(file) #读取工作簿 ws wb.sheets()[0] #选第一个工作表 data [] for row in range(7, ws.nrows): name ws.cell(row, 1).value.strip() #科室名称 total1 ws.cell(row, 2…...
从零开始实现大语言模型(三):Token Embedding与位置编码
1. 前言 Embedding是深度学习领域一种常用的类别特征数值化方法。在自然语言处理领域,Embedding用于将对自然语言文本做tokenization后得到的tokens映射成实数域上的向量。 本文介绍Embedding的基本原理,将训练大语言模型文本数据对应的tokens转换成Em…...
视频怎么压缩变小?最佳视频压缩器
即使在云存储和廉价硬盘空间时代,大视频文件使用起来仍然不方便。无论是存储、发送到电子邮件帐户还是刻录到 DVD,拥有最好的免费压缩软件可以确保您快速缩小文件大小,而不必担心视频质量下降。继续阅读以探索一些顶级最佳 免费视频压缩器选项…...
LLM - 绝对与相对位置编码 与 RoPE 旋转位置编码 源码
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/140281680 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 Transformer 是基于 MHSA (多头自注意力),然而,MHSA 对于位置是不敏感…...
B3917 [语言月赛 202401] 小跳蛙
OK 挠~ stop here~ 好啊,现在呢,把手头的事情先放一放啊,我们来做道练习 OK? 好啊来: 小跳蛙 题目描述 有 𝑛−1 只小跳蛙在池塘中,依次被编号为 1,2,⋯ ,𝑛−1。池塘里有 &am…...
Bash ——shell
Bash作为用户与操作系统之间的接口,让用户通过命令行输入各种指令来控制和操作计算机系统。 shell的两种解释: 1.linux命令解释器 Terminal 终端 ——》shell命令 ——》 Linux kernel (内核) Linux内核的作用: 1.…...
PyTorch复现PointNet——模型训练+可视化测试显示
因为项目涉及到3D点云项目,故学习下PointNet这个用来处理点云的神经网络 论文的话,大致都看了下,网络结构有了一定的了解,本博文主要为了下载调试PointNet网络源码,训练和测试调通而已。 我是在Anaconda下创建一个新的…...
分享五款软件,成为高效生活的好助手
给大家分享一些优秀的软件工具,是一件让人很愉悦的事情,今天继续带来5款优质软件。 1.图片放大——Bigjpg Bigjpg是一款图片放大软件,采用先进的AI算法,能够在不损失图片质量的前提下,将低分辨率图片放大至所需尺寸。无论…...
代码随想录算法训练营DAY58|101.孤岛的总面积、102.沉没孤岛、103. 水流问题、104.建造最大岛屿
忙。。。写了好久。。。。慢慢补吧。 101.孤岛的总面积 先把周边的岛屿变成水dfs def dfs(x, y, graph, s):if x<0 or x>len(graph) or y<0 or y>len(graph[0]) or graph[x][y]0:return sgraph[x][y]0s1s dfs(x1, y, graph, s)s dfs(x-1, y, graph, s)s dfs(…...
韦尔股份:深蹲起跳?
利润大增7倍,是反转信号还是回光返照? 今天我们聊聊光学半导体龙头——韦尔股份。 上周末,韦尔股份发布半年业绩预告,预计上半年净利润13至14亿,同比增幅高达 754%至 819%。 然而,回首 2023 年它的净利仅 …...
docs | 使用 sphinx 转化rst文件为html文档
1. 效果图 book 风格。 优点: 极简风格右边有标题导航左侧是文件导航,可隐藏 2. 使用方式 reST 格式,比markdown格式更复杂。 推荐使用 book 风格。 文档构建工具是 sphinx,是一个python包。 $ pip3 list | grep -i Sphinx …...
【ChatGPT 消费者偏好】第二弹:ChatGPT在日常生活中的使用—推文分享—2024-07-10
今天的推文主题还是【ChatGPT & 消费者偏好】 第一篇:哪些动机因素和技术特征的组合能够导致ChatGPT用户中高和低的持续使用意图。第二篇:用户对ChatGPT的互动性、性能期望、努力期望以及社会影响如何影响他们继续使用这些大型语言模型的意向&#x…...
Webpack配置及工作流程
Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler)。当Webpack处理应用程序时,它会在内部构建一个依赖图(dependency graph),该图会映射项目所需的每个模块,并生成一个或多…...
华为ensp实现防火墙的区域管理与用户认证
实验环境 基于该总公司内网,实现图片所在要求 后文配置请以本图为准 接口配置与网卡配置 1、创建vlan 2、防火墙g0/0/0与云页面登录 登录admin,密码Admin123,自行更改新密码 更改g0/0/0口ip,敲下命令service-manage all permit 网卡配置…...
深入解析 Laravel 策略路由:提高应用安全性与灵活性的利器
引言 Laravel 是一个功能强大的 PHP Web 应用框架,以其优雅和简洁的语法而受到开发者的喜爱。在 Laravel 中,路由是应用中非常重要的一部分,它负责将用户的请求映射到相应的控制器方法上。Laravel 提供了多种路由方式,其中策略路…...
Java | Leetcode Java题解之第228题汇总区间
题目: 题解: class Solution {public List<String> summaryRanges(int[] nums) {List<String> ans new ArrayList<>();for (int i 0, j, n nums.length; i < n; i j 1) {j i;while (j 1 < n && nums[j 1] num…...
使用Simulink基于模型设计(三):建模并验证系统
可以对系统结构中的每个组件进行建模,以表示该组件的物理行为或功能行为。通过使用测试数据对组件进行仿真,以验证它们的基本行为。 打开系统布局 对各个组件进行建模时,需要从大局上把握整个系统布局。首先加载布局模型。这里以simulink自…...
基于go 1.19的站点模板爬虫
好像就三步: 1 建立http连接 2 解析html内容 3 递归遍历 创建一个基于 Go 1.19 的网站模板爬虫主要涉及几个步骤,包括设置 HTTP 客户端来获取网页内容、解析 HTML 来提取所需的数据,以及处理可能的并发和错误。下面我会给出一个简单的例子来说明如何…...
0基础学会在亚马逊云科技AWS上搭建生成式AI云原生Serverless问答QA机器人(含代码和步骤)
小李哥今天带大家继续学习在国际主流云计算平台亚马逊云科技AWS上开发生成式AI软件应用方案。上一篇文章我们为大家介绍了,如何在亚马逊云科技上利用Amazon SageMaker搭建、部署和测试开源模型Llama 7B。下面我将会带大家探索如何搭建高扩展性、高可用的完全托管云原…...
[PaddlePaddle飞桨] PaddleOCR图像小模型部署
PaddleOCR的GitHub项目地址 推荐环境: PaddlePaddle > 2.1.2 Python > 3.7 CUDA > 10.1 CUDNN > 7.6pip下载指令: python -m pip install paddlepaddle-gpu2.5.1 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install paddleocr2.7…...
C语言 | Leetcode C语言题解之第227题基本计算题II
题目: 题解: int calculate(char* s) {int n strlen(s);int stk[n], top 0;char preSign ;int num 0;for (int i 0; i < n; i) {if (isdigit(s[i])) {num num * 10 (int)(s[i] - 0);}if (!isdigit(s[i]) && s[i] ! || i n - 1) {s…...
kafka.common.KafkaException: Socket server failed to bind to xx:9092
部署分布式集群的时候遇到的错误。 解决方案: 修改config下的server.properties,添加 listenersPLAINTEXT://:9092 advertised.listenersPLAINTEXT://自己的服务器ip:9092 然后重新启动,检查进程是否存在ps -aux | grep kafka。 成功启动。...
【JS+H5+CSS实现烟花特效】
话不多说直接上代码 注意:背景图路径是picture/star.jpg,自己在同级目录先创键picture目录再下载一张图片命名为star.jpg HTML: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"vi…...
uniapp小程序使用webview 嵌套 vue 项目
uniapp小程序使用webview 嵌套 vue 项目 小程序中发送 <web-view :src"urlSrc" message"handleMessage"></web-view>export default {data() {return {urlSrc: "",};},onLoad(options) {// 我需要的参数比较多 所以比较臃肿// 获取…...
沭阳做网站/什么是seo搜索
General import matplotlib import matplotlib.pyplot as plt # plt. ... plt.grid(True) plt.show() Variation bar plt.bar(X, Y) # 1-D, 1-D scatter plt.scatter(X, Y, s3) # 1-D, 1-D3-D barhttps://www.jianshu.com/p/bb8... ......
网站建设价钱/贵州二级站seo整站优化排名
嵌入式介绍与应用1 概念桌面对比2 特点3 发展历史3.1 计算机发展3.2 嵌入式发展4 开发能力要求5 应用6 规模参考1 概念 嵌入式系统由硬件和软件组成。是能够独立进行运作的器件。其软件内容只包括软件运行环境及其操作系统。硬件内容包括信号处理器、存储器、通信模块等在内的…...
江苏省建设协会网站/竞彩足球最新比赛
下午要用Matlab做图像处理,下载了Matlab 2016b的安装包之后,开始安装,“sudo ./install” 无论如何都失败,找了一些文章,方法都有问题。运行install之后,Terminal中闪过以下几个代码之后:Prepar…...
企业建网站公司多少钱/百度指数app
五、学习LLC谐振变换电路的工作原理 在具有电阻R、电感L和电容C元件的交流电路中,电路两端的电压与其中电流相位一般是不同的。如果调节电路元件(L或C)的参数或电源频率,可以使它们相位相同,整个电路呈现为纯电阻性。…...
网络 企业网站/互联网广告代理
一、单位的换算 1字节(B)8bit 1KB1024字节 1MB1024KB 1GB1024MB 1TB1024GB 通信单位中 K千 , M 百万 计算机单位中 K2^10 , M 2^20 倍数刚好是1.024的幂 ^ 为次方; /为除 ; *为乘 ; (X/X)为单位 二、计算总线数据传输速率 总线数据传输速率时钟频率(Mhz)/每个总线包…...
网站接口怎么做/东莞互联网推广
1. 问题 为描述方便,我们简化下问题。 {assign var"star" value"胡哥;吴秀波;王宝强;三小只"} {$star|regex_replace:/;/:/} 在smarty模板中,将“;”(半角分号)替换为“/”。在看这段代码时,第…...