1.C#中的反射
1. 获取类型信息
你可以使用Type
类来获取任何类型的信息,包括其成员(字段、属性、方法等)。
cs
Type myType = typeof(MyClass);
2. 动态创建对象
使用Activator
类,你可以在运行时动态创建类型的实例。
cs
object myObject = Activator.CreateInstance(typeof(MyClass));
3. 访问类型成员
你可以使用Type
对象来访问类型的成员,包括字段、属性和方法。
访问字段:
cs
FieldInfo fieldInfo = myType.GetField("myField", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo.SetValue(myObject, "New Value");
访问属性:
cs
PropertyInfo propertyInfo = myType.GetProperty("myProperty");
propertyInfo.SetValue(myObject, "New Value", null);
调用方法:
cs
MethodInfo methodInfo = myType.GetMethod("myMethod");
methodInfo.Invoke(myObject, new object[] { "Argument" });
4. 泛型和反射
反射也可以与泛型一起使用,这在处理不确定类型的集合或泛型方法时非常有用。
cs
Type genericType = typeof(List<>).MakeGenericType(typeof(string));
object list = Activator.CreateInstance(genericType);
5. 性能考虑
虽然反射提供了极大的灵活性,但它通常比直接代码调用慢,因为它涉及到更多的间接性和运行时类型检查。因此,在性能敏感的应用中,应谨慎使用反射。
6. 安全性和权限
使用反射时,你可能需要考虑安全性和权限问题,因为反射可以访问私有成员,这可能违反了封装原则。
7. 绑定标志
在访问类型的成员时,BindingFlags
枚举非常有用,它允许你指定要搜索的成员的类型(如公共的、私有的、实例的或静态的)。
cs
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
PropertyInfo propertyInfo = myType.GetProperty("myProperty", bindingFlags);
2.基本数据类型,分别占的字节长度
- bool:1字节(在某些平台可能是2字节)
- byte:1字节
- sbyte:1字节
- char:2字节
- short:2字节
- ushort:2字节
- int:4字节
- uint:4字节
- long:8字节
- ulong:8字节
- float:4字节
- double:8字节
- decimal:16字节(.NET中特有的数据类型)
3.为什么结构体属于值类型?
-
存储位置:值类型的实例直接存储在栈(stack)上或者被嵌入到包含它们的类型中。这意味着每个变量都直接包含数据的副本,而不是指向数据的引用。
-
赋值和参数传递:当值类型的变量被赋值给另一个变量,或者作为参数传递给方法时,会发生值的复制。这与引用类型不同,引用类型在赋值或传递时共享对同一对象的引用。
-
默认行为:值类型在方法调用结束后,其值会直接返回给调用者,不需要额外的引用或指针操作。
-
不可变性 :虽然C#中的值类型可以被修改,但它们本身不支持不可变模式。不过,它们可以被设计成表现得像不可变类型,通过确保所有字段都是
readonly
的,并且没有公开的setter方法。 -
内存管理:值类型的内存管理是自动的,它们在创建时自动分配在栈上,当超出作用域时自动释放。这与引用类型不同,引用类型需要垃圾回收器(GC)来管理它们的生命周期。
-
简单性:值类型的设计意图是简单和轻量级,适合于小型数据结构,如基本数据类型和简单的聚合数据。
-
性能:对于小的值类型,使用栈内存可以减少垃圾回收的开销,因为栈内存的分配和回收速度比堆内存快。
-
安全性:值类型提供了更好的线程安全性,因为每个线程都有自己的栈副本,减少了共享状态和同步的需求。
-
语义清晰:将结构体定义为值类型可以清晰地表达出它们的语义,即它们代表的是数据的值,而不是数据的引用。
-
继承和多态:值类型不支持继承和多态,这与它们作为自包含的数据结构的设计理念相符合。
4.多线程、同步、异步、线程安全
多线程(Multithreading)
多线程是指在同一个程序中并行运行两个或多个线程。线程是操作系统能够进行运算调度的最小单位,也是被系统独立调度和分派的基本单位。在多核处理器上,多线程可以同时在不同的物理核心上运行,实现真正的并行处理。
同步(Synchronization)
同步是指在多线程环境中,协调多个线程对共享资源的访问,以防止发生冲突和数据不一致的情况。同步机制确保在任何时刻,只有一个线程能够访问特定的资源。常见的同步机制包括互斥锁(Mutex)、信号量(Semaphore)、监视器(Monitor)和同步块(Synchronized block)。
异步(Asynchronous)
异步编程是一种编程方式,允许程序在等待一个长时间运行的任务完成时,继续执行其他任务。异步操作不会阻塞调用线程,而是在后台执行,通常在完成时通过回调函数、事件或Future对象来通知调用者。异步编程可以提高应用程序的响应性和吞吐量。
线程安全(Thread Safety)
线程安全是指代码在多线程环境中能够正确执行,不会出现数据不一致或状态错误的情况。线程安全的代码或库可以被多个线程同时使用,而不会出现问题。为了实现线程安全,开发者需要确保代码在访问共享资源时正确地使用同步机制,避免出现竞态条件(Race Condition)。
线程安全与同步的关系
线程安全通常需要通过同步来实现。同步是确保线程安全的一种手段,通过控制对共享资源的访问,防止多个线程同时修改同一数据,从而避免数据竞争和不一致性。
实现线程安全的方法
- 不可变对象:创建不可变的对象,因为它们的状态在创建后不能改变,所以天然是线程安全的。
- 锁定:使用互斥锁或其他同步机制来保护对共享资源的访问。
- 原子操作:使用原子变量和原子操作,它们保证在多线程环境中的单个操作是不可分割的。
- 线程局部存储:使用线程局部变量,每个线程都有自己的变量副本,避免了共享。
- 无状态对象:设计无状态的对象,它们不存储任何状态信息,每次调用都使用新的输入参数。
- 使用线程安全的数据结构 :使用已经实现线程安全的数据结构,如
ConcurrentHashMap
。