1. 什么是 Unsafe 代码?
Unsafe 代码是 C# 中允许直接操作内存和指针的特性,它绕过了 CLR(公共语言运行时)的一些安全检查。在 Unity 中,使用 Unsafe 代码可以:
-
直接操作内存地址
-
使用指针类型
-
进行底层内存操作
-
提高特定场景下的性能
2. 启用 Unsafe 代码
在 Unity 项目中,需要在以下位置启用:
-
Player Settings:
-
Edit → Project Settings → Player
-
Other Settings → Configuration
-
勾选 "Allow 'unsafe' Code"
-
-
Assembly Definition(如果需要):
json
{
"allowUnsafeCode": true
}
3. Unsafe 上下文
基本语法
csharp
unsafe class UnsafeClass
{
// 类中的不安全代码
}
unsafe void UnsafeMethod()
{
// 方法中的不安全代码
}
void SafeMethod()
{
unsafe
{
// 代码块中的不安全代码
}
}
4. 指针基础
声明和使用指针
csharp
unsafe void PointerBasics()
{
int value = 42;
int* pointer = &value; // & 获取地址
Debug.Log($"值: {value}");
Debug.Log($"地址: {(long)pointer:X}");
Debug.Log($"通过指针访问: {*pointer}"); // * 解引用
*pointer = 100; // 通过指针修改值
Debug.Log($"修改后的值: {value}");
}
指针类型
csharp
unsafe void PointerTypes()
{
// 基本类型指针
int* intPtr;
float* floatPtr;
byte* bytePtr;
// void 指针(无类型指针)
void* voidPtr;
// 结构体指针
Vector3* vectorPtr;
}
5. fixed 关键字
固定托管变量
csharp
unsafe void FixedExample()
{
int[] array = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 使用 fixed 固定数组在内存中的位置
fixed (int* ptr = array)
{
for (int i = 0; i < 10; i++)
{
Debug.Log($"array[{i}] = {*(ptr + i)}");
// 修改数组元素
*(ptr + i) = i * 10;
}
}
// 固定多个变量
fixed (int* p1 = &array[0], p2 = &array[5])
{
// 同时操作多个指针
}
}
固定字符串
csharp
unsafe void FixedString()
{
string text = "Hello";
fixed (char* p = text)
{
for (int i = 0; i < text.Length; i++)
{
Debug.Log($"字符 {i}: {*(p + i)}");
}
}
}
6. 指针运算
csharp
unsafe void PointerArithmetic()
{
int[] numbers = new int[5] { 10, 20, 30, 40, 50 };
fixed (int* ptr = numbers)
{
int* current = ptr;
// 指针加法
current += 2; // 移动到第三个元素
Debug.Log($"当前位置的值: {*current}"); // 30
// 指针减法
current -= 1; // 回到第二个元素
Debug.Log($"新位置的值: {*current}"); // 20
// 指针比较
int* start = ptr;
int* end = ptr + 4;
Debug.Log($"start < end: {start < end}"); // true
// 指针差
long difference = end - start;
Debug.Log($"指针差: {difference}"); // 4
}
}
7. 结构体指针
csharp
unsafe struct Point3D
{
public float x;
public float y;
public float z;
}
unsafe void StructPointerExample()
{
Point3D point = new Point3D { x = 1.0f, y = 2.0f, z = 3.0f };
Point3D* ptr = &point;
// 访问结构体成员
Debug.Log($"x: {ptr->x}"); // 使用 -> 操作符
Debug.Log($"y: {(*ptr).y}"); // 使用 (*ptr). 语法
// 修改结构体成员
ptr->x = 10.0f;
Debug.Log($"修改后的 x: {point.x}");
}
8. 栈内存分配
csharp
unsafe void StackAllocExample()
{
// 在栈上分配内存(避免GC)
int* numbers = stackalloc int[5];
for (int i = 0; i < 5; i++)
{
numbers[i] = i * i;
}
// 读取数据
for (int i = 0; i < 5; i++)
{
Debug.Log($"numbers[{i}] = {numbers[i]}");
}
// 栈分配结构体数组
Vector3* vectors = stackalloc Vector3[3];
for (int i = 0; i < 3; i++)
{
vectors[i] = new Vector3(i, i * 2, i * 3);
}
}
9. Unity 中的实际应用
高性能数学运算
csharp
unsafe void ProcessVertices(Vector3[] vertices, Matrix4x4 matrix)
{
fixed (Vector3* verticesPtr = vertices)
{
Vector3* current = verticesPtr;
int length = vertices.Length;
for (int i = 0; i < length; i++)
{
// 使用指针进行矩阵变换(避免临时变量)
current->x = matrix.m00 * current->x + matrix.m01 * current->y + matrix.m02 * current->z + matrix.m03;
current->y = matrix.m10 * current->x + matrix.m11 * current->y + matrix.m12 * current->z + matrix.m13;
current->z = matrix.m20 * current->x + matrix.m21 * current->y + matrix.m22 * current->z + matrix.m23;
current++;
}
}
}
图像处理
csharp
unsafe void ProcessTexture(Texture2D texture)
{
var colors = texture.GetPixels32();
fixed (Color32* colorsPtr = colors)
{
Color32* current = colorsPtr;
int pixelCount = colors.Length;
for (int i = 0; i < pixelCount; i++)
{
// 灰度化处理
byte gray = (byte)((current->r * 0.299f + current->g * 0.587f + current->b * 0.114f));
current->r = gray;
current->g = gray;
current->b = gray;
current++;
}
}
texture.SetPixels32(colors);
texture.Apply();
}
10. 与 NativeArray 和 Jobs System 结合
csharp
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
unsafe void ProcessWithJobs()
{
NativeArray<float> data = new NativeArray<float>(1000, Allocator.TempJob);
// 获取原生指针
float* dataPtr = (float*)data.GetUnsafePtr();
// 使用指针进行初始化
for (int i = 0; i < data.Length; i++)
{
dataPtr[i] = i * 0.1f;
}
// 创建并调度Job
var job = new ParallelProcessingJob
{
Data = data,
DataPtr = dataPtr
};
JobHandle handle = job.Schedule(data.Length, 64);
handle.Complete();
data.Dispose();
}
struct ParallelProcessingJob : IJobParallelFor
{
public NativeArray<float> Data;
[NativeDisableUnsafePtrRestriction]
public unsafe float* DataPtr;
public void Execute(int index)
{
// 在Job中直接使用指针
DataPtr[index] = Mathf.Sin(DataPtr[index]);
}
}
11. 内存复制和操作
csharp
unsafe void MemoryOperations()
{
// 使用 Buffer.MemoryCopy(高性能内存复制)
byte[] source = new byte[1024];
byte[] destination = new byte[1024];
fixed (byte* srcPtr = source, dstPtr = destination)
{
// 复制内存
System.Buffer.MemoryCopy(srcPtr, dstPtr, destination.Length, source.Length);
}
// 归零内存区域
int[] array = new int[100];
fixed (int* ptr = array)
{
// 使用指针快速清零
UnsafeUtility.MemClear(ptr, array.Length * sizeof(int));
}
}
12. 安全注意事项
常见问题
-
内存泄漏:忘记释放非托管内存
-
悬垂指针:访问已释放的内存
-
缓冲区溢出:访问超出分配范围的内存
-
内存对齐:未对齐的内存访问可能导致性能问题或崩溃
最佳实践
csharp
unsafe void SafeUnsafePractices()
{
// 1. 总是使用 fixed 固定托管变量
// 2. 检查指针是否为 null
// 3. 验证数组边界
// 4. 避免在栈上分配大内存
// 5. 使用 using 语句管理 NativeArray 等资源
try
{
int[] data = new int[100];
fixed (int* ptr = data)
{
// 安全地使用指针
if (ptr != null)
{
// 操作指针
}
}
}
catch (System.Exception e)
{
Debug.LogError($"Unsafe 代码错误: {e.Message}");
}
}
13. 性能对比
csharp
void PerformanceComparison()
{
const int SIZE = 1000000;
float[] array = new float[SIZE];
// 安全版本
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < SIZE; i++)
{
array[i] = Mathf.Sqrt(array[i]);
}
sw.Stop();
Debug.Log($"安全版本: {sw.ElapsedMilliseconds}ms");
// Unsafe 版本
sw.Restart();
unsafe
{
fixed (float* ptr = array)
{
for (int i = 0; i < SIZE; i++)
{
ptr[i] = Mathf.Sqrt(ptr[i]);
}
}
}
sw.Stop();
Debug.Log($"Unsafe 版本: {sw.ElapsedMilliseconds}ms");
}
总结
使用场景
-
性能关键代码:需要极致优化的算法
-
原生代码交互:与 C/C++ 库交互
-
内存操作:需要直接内存操作的任务
-
数据处理:大规模数据处理和变换
注意事项
-
平台兼容性:某些平台可能限制 Unsafe 代码
-
调试难度:指针错误难以调试
-
团队协作:确保团队成员理解 Unsafe 代码
-
代码审查:严格审查所有 Unsafe 代码
Unsafe 代码是强大的工具,但应该谨慎使用。在 Unity 中,通常只有在性能瓶颈确实存在且其他优化手段无效时,才应考虑使用 Unsafe 代码。
531

被折叠的 条评论
为什么被折叠?



