Unity ECS记录

2023-11-05

参考:What are Blob Assets?
参考:Converting scene data to DOTS
参考:unity_dots_packages
参考:unity_entities_package_documents


前言

我之前写过文章Entity Component System与Entity Component介绍EC系统和ECS系统的区别,几年前的Unity和UE还都是用的EC框架,但这几年我看他们都开始往ECS架构调整了,Unity推出了DOTS系统,UE5里推出了Large Entities of Entities with Mass,ECS架构对内存和数据存储更友好一些,两个引擎都做出这个操作也是很合理的。

Unity的ECS框架貌似是让Editor下保留原本的EC框架,打包Runtime后变成ECS框架,然后还提供了一些方法和接口,用于把老的EC框架的Unity项目,过渡到新的ECS框架。



DOTS

DOTS全称为Data-Oriented Technology Stack,它其实是一种Unity使用的技术,具体的代码Unity把它拆分到了Packages里,一共有这么些,ECS其实是Unity的DOTS的一部分:
在这里插入图片描述

这里的Job System和Burst Compiler是Unity早就部署好的Package,简单介绍一下:

  • Job System:为了方便写多线程的代码,Unity内部的C++源码创了Job System,同时把接口暴露在了C#层,所以用户也可以在C#这边使用它的Job System
  • Burst Compiler:Clang/LLVM(Low Level Virtual Machine)是C++常规的编译器,LLVM是一个虚拟机,类似于编译器编译语言时的背景,而Clang是基于LLVM的编译器,可以编译C、C++、Objective-C、Objective-C++。Burst Compiler就是基于LLVM,可以把C#这边写的job代码编译成Machine Code

Entity Package

With ECS, we are moving from object-oriented to a data-oriented design.

Entity Package是基于Unity已有的Job System和Burst Compiler的基础上开发的ECS系统,相关内容都放到了Unity的名为entities的package里,命名空间为Unity.Entities。此Package还包含了Unity Live Link,有了它,可以直接在Unity Editor里改动,然后马上看到在runtime下的情况。


blob asset

A blob asset is an immutable data structure stored in unmanaged memory. Blob assets can contain primitive types, strings, structs, arrays, and arrays of arrays. Arrays and structs must only contain blittable types. Strings must be of type “BlobString” (or a specialized unmanaged string type such as “NativeString64”.

BLOB,代表Binary Large Object,总之,blob asset就是Unity在C++上分配的一段内存,这里的asset并不代表传统意义上的资源,而是表示的内存Data。Unity早在推出ECS包之前,就开始着手Blob系统的构建了,它可以帮助用户把数据放到C++层,从而可以在C#层高效的读写数据,同时支持Job System,随着Unity推出Entity系统,它可以帮助来存Component里的数据。

我总结了一下BLOB asset相较于C++的堆内存分配、以及Unity的NativeContainer的区别:

  1. BLOB asset在系统里分布的内存是随时可以relocate的,所以无法使用裸指针去记录Blob asset的地址,需要使用blob asset类提供的相较于固定memory address的offset,获取对应的指针的reference
  2. BLOB asset里的写入,不会像native containers一样,需要进行安全检查
  3. blob asset是runtime只读的,各个Job都可以访问,它特别适合存放那种Runtime下完全不可能更改的Data,比如动画的Curve数据、导航网格等信息

在这里插入图片描述


blob asset里的数据

Unlike native containers, blob assets have no safety checks against concurrent writes.

其数据都是runtime下只读的,像struct、class、string这种非primitive的都有对应的blob的表示方法,比如:

public struct MyPointAsset
{
    public BlobArray<float> array1;
}

由于blob asset的relocatable的特性,Unity提供了BlobBuilder来创建和管理blob asset,它会记录blob asset相对于固定内存地址的offset,也会记录offset的变化,让用户获取对应的数据。


BlobBuilder

BlobBuilder对象负责创建blob asset,即在C++上分配一段内存,流程如下:

  • 创建代表blob asset对应的struct
  • Create a BlobBuilder object
  • 调用BlobBuilder的ConstructRoot<T>方法,T代表blob asset对应的struct,其实就是Allocate了内存,返回对应的指针而已
  • 填充struct里的primitive数据,比如float、int这种的
  • 对于struct里涉及堆内存的数据,比如struct、class、string又要分配对应的内存再初始化
  • 最后Dispose BlobBuilder对象

别小看这个流程,实际写代码的时候要严格参考这个步骤,这里面可以通过BlobBuilder.CreateBlobAssetReference来创建blob asset的引用


创建blob asset例子

先举一个最简单的例子,struct中只有primitive类型:

// 数据必须是struct
struct MarketData
{
    public float PriceOranges;
    public float PriceApples;
}

// 返回BlobAssetReference<MarketData对象
BlobAssetReference<MarketData> CreateMarketData()
{
    // Create a new builder that will use temporary memory to construct the blob asset
    var builder = new BlobBuilder(Allocator.Temp);

    // Construct the root object for the blob asset. Notice the use of `ref`.
    // 注意写法
    ref MarketData marketData = ref builder.ConstructRoot<MarketData>();

    // Now fill the constructed root with the data:
    // Apples compare to Oranges in the universally accepted ratio of 2 : 1 .
    marketData.PriceApples = 2f;
    marketData.PriceOranges = 4f;

    // Now copy the data from the builder into its final place, which will
    // use the persistent allocator
    // 使用BlobAssetReference作为长期引用
    var result = builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);

    // Make sure to dispose the builder itself so all internal memory is disposed.
    builder.Dispose();
    return result;
}

BlobArray例子

然后是当数据struct里有数组的情况:

struct Hobby
{
    public float Excitement;
    public int NumOrangesRequired;
}

// 数据里存了个数组
struct HobbyPool
{
    public BlobArray<Hobby> Hobbies;
}

BlobAssetReference<HobbyPool> CreateHobbyPool()
{
	// 没啥特别的
    var builder = new BlobBuilder(Allocator.Temp);
    ref HobbyPool hobbyPool = ref builder.ConstructRoot<HobbyPool>();

    // Allocate enough room for two hobbies in the pool. Use the returned BlobBuilderArray
    // to fill in the data.
    // 通过builder为数组分配uninitialized内存
    const int numHobbies = 2;
    BlobBuilderArray<Hobby> arrayBuilder = builder.Allocate(
        ref hobbyPool.Hobbies,
        numHobbies
    );

    // Initialize数组元素

    // An exciting hobby that consumes a lot of oranges.
    arrayBuilder[0] = new Hobby
    {
        Excitement = 1,
        NumOrangesRequired = 7
    };

    // A less exciting hobby that conserves oranges.
    arrayBuilder[1] = new Hobby
    {
        Excitement = 0.2f,
        NumOrangesRequired = 2
    };

	// 没啥特别的
    var result = builder.CreateBlobAssetReference<HobbyPool>(Allocator.Persistent);
    builder.Dispose();
    return result;
}

BlobString例子

然后是数组里有string的情况,也差不多:

struct CharacterSetup
{
    public float Loveliness;
    public BlobString Name;
}

BlobAssetReference<CharacterSetup> CreateCharacterSetup(string name)
{
    var builder = new BlobBuilder(Allocator.Temp);
    ref CharacterSetup character = ref builder.ConstructRoot<CharacterSetup>();

    character.Loveliness = 9001; // it's just a very lovely character

    // Create a new BlobString and set it to the given name.
    builder.AllocateString(ref character.Name, name);

    var result = builder.CreateBlobAssetReference<CharacterSetup>(Allocator.Persistent);
    builder.Dispose();
    return result;
}

BlobPtr例子

如果struct里有指针,应该用BlobPtr,写法如下:

// struct里是一个指针、一个数组
struct FriendList
{
    public BlobPtr<BlobString> BestFriend;
    public BlobArray<BlobString> Friends;
}

BlobAssetReference<FriendList> CreateFriendList()
{
    var builder = new BlobBuilder(Allocator.Temp);
    ref FriendList friendList = ref builder.ConstructRoot<FriendList>();

    const int numFriends = 3;
    var arrayBuilder = builder.Allocate(ref friendList.Friends, numFriends);
    builder.AllocateString(ref arrayBuilder[0], "Alice");
    builder.AllocateString(ref arrayBuilder[1], "Bob");
    builder.AllocateString(ref arrayBuilder[2], "Joachim");

    // Set the best friend pointer to point to the second array element.
    // 指针需要调用SetPointer函数
    builder.SetPointer(ref friendList.BestFriend, ref arrayBuilder[2]);

    var result = builder.CreateBlobAssetReference<FriendList>(Allocator.Persistent);
    builder.Dispose();
    return result;
}

struct的例子

对于struct而言,其实直接赋值就可以了,比如:

struct MyData
{
    public Vector3 oneVector3;
}

// 用的时候直接这么写, 就行了, struct已经会在ConstructRoot里就完成了allocate工作
// 在这里只是进行初始化操作
root.oneVector3 = new Vector3(3, 3, 3);

或者用BlobPtr也行,写法上麻烦一些:

struct MyData
{
    public BlobPtr<Vector3> oneVector3;
}

// BlobPtr需要单独Allocate
ref Vector3 oneVector3 = ref builder.Allocate(ref root.oneVector3);
oneVector3 = new Vector3(3, 3, 3);

class的例子


在Component上获取blob asset

之前在创建BlobAsset时返回了一个BlobAssetReference<T>的对象,可以把该引用存到Component里,

// Hobbies属于ComponentData, 里面是一个HobbyPool对象的引用
struct Hobbies : IComponentData
{
    public BlobAssetReference<HobbyPool> Blob;
}

float GetExcitingHobby(ref Hobbies component, int numOranges)
{
    // Get a reference to the pool of available hobbies. Note that it needs to be passed by
    // reference, because otherwise the internal reference in the BlobArray would be invalid.
    ref HobbyPool pool = ref component.Blob.Value;

    // Find the most exciting hobby we can participate in with our current number of oranges.
    float mostExcitingHobby = 0;
    for (int i = 0; i < pool.Hobbies.Length; i++)
    {
        // This is safe to use without a reference, because the Hobby struct does not
        // contain internal references.
        var hobby = pool.Hobbies[i];
        if (hobby.NumOrangesRequired > numOranges)
            continue;
        if (hobby.Excitement >= mostExcitingHobby)
            mostExcitingHobby = hobby.Excitement;
    }

    return mostExcitingHobby;
}



blob asset作为参数或返回值

Blob assets must always be accessed and passed by reference using the ref keyword or using BlobAssetReference

可以使用ref,也可以使用BlobAssetReference


Archetypes

A unique combination of component types is called an EntityArchetype

Arche这个单词有始源、初始的意思,在这里指的是Component的组合类型,有相同的Component的Entity属于同一个ArcheType,如下图所示:
在这里插入图片描述

UnityEditor在Dots里还提供了ArcheWindow,可以看ECS里各个类型ArcheType的entities的内存占用情况:
在这里插入图片描述
更多的窗口介绍参考Archetypes window


Memory Chunks

ECS allocates memory in “chunks”, each represented by an ArchetypeChunk object.

Chunk其实就是内存块而已,同一个Chunk里存的都是相同的ArcheType的Entity的Component数据,如下图所示,同一个类型的ArcheType的Entity的Component数据,可以对应多个Chunk:
在这里插入图片描述
最重要的一点就是:Chunk里存的是Component数据,而且是按照AAAABBBBCCCCDDDD的顺序存的,里面的相同的Component数据的连续分布在内存里的,这完全就是ECS里存储C的方法。用这种存储方法,能够快速的找到拥有指定Component组合的所有Entityes的数据,

补充一点,同种ArcheType的Entity在Chunk里的分布顺序是没有什么讲究的,当Entity被创建或删除时,为了保证Chunks remain tightly packed,在Chunk里的处理逻辑如下:

  • Entity从无到有创建时,它会直接分配内存到对应ArcheType的第一个有空余位置的Chunk的最后的空余位置上
  • Entity改变了ArcheType时,它原本在内存上的位置,会被该Chunk的最后一个Entity所替换(相当于把数组最后一个元素,移到了前面),然后把该Entity移到新的Chunk的最后空余位置

The values of shared components in an archetype also determine which entities are stored in which chunk. All of the entities in a given chunk have the exact same values for any shared components. If you change the value of any field in a shared component, the modified entity moves to a different chunk, just as it would if you changed that entity’s archetype. A new chunk is allocated, if necessary.

额外还需要注意,对于共享特定Component数据的相同ArcheType的Entity,它们都会存在同一个Chunk里,也就是说Chunk里存在的都是拥有相同Component数据的Entity,一旦某个Entity的Component数据单独被修改,那么它自己会挪到别的Chunk里(如果是独一无二的Component数据,可能一个Entity对应唯一一个Chunk)


Entity queries

https://docs.unity3d.com/Packages/com.unity.entities@0.50/manual/ecs_core.html



Converting scene data to DOTS

Unity官方还专门出了个视频,来介绍这个东西,方便用户把原本的EC架构下的游戏工程转化为DOTS架构的游戏,有点意思。Unity这边现在有两种架构,Editor下仍然是原本的EC架构,而Runtime下Unity把它转成了ECS架构。也就是说,Unity的转化功能把Editor下的GameObject转化成了Runtime下的Entity

简单介绍一下吧,这里有个SubScene的概念,比如在默认Unity场景里加了个Cube的GameObject,然后给它上面加了个自我旋转的MonoBehaviour脚本:
Cube是人为
此时的Hierarchy为:
在这里插入图片描述
Unity在进行转换时,对于所有出现在hierarchy里的Tree,应该会根据需要(我理解的是包含MonoBehaviour的Tree),把它放到对应的SubScene里:
在这里插入图片描述

此时要把现有的Rotate这个MonoBehaviour转化为Unity的ECS系统,需要把逻辑拆分到S里,数据拆分到C里,所以此时会有三个类:

  • 一个类继承于原本的MonoBehaviour类,Unity会负责把它转化为ECS系统里
  • 一个新的数据类,记录了Component的数据,在这里就是旋转速度
  • 一个行为类,记录了在System里执行的代码

所以最终的三个类代码如下:

// 原本的类需要多继承一个接口
public class RotateToBeConverted : MonoBehaviour, IConvergGameObjectToEntity
{
	public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
	{
		dstManager.AddComponentData(entity, new RotateComponentData { RadiansPerSecond = this.RadiansPerSecond; });
		dstManager.AddComponentData(entity, new RotationEulerXYZ());// 这个应该是Unity自带的Component
	}
}

public class RotateComponentData : IComponentData
{
	public float RadiansPerSecond;
}

public class RotateSystem : ComponentSystem
{
	protected override void OnUpdate()
	{
		Entities.ForEach((ref RotateComponentData rotateData, ref RotationEulerXYZ rotation) => 
		{
			rotation.y += rotateData.RadiansPerSecond * Time.deltaTime;
		});
	}
}

这个例子是指的正常的Component类,不过如果Component类里包含了Spawn其他GameObject的代码,那么需要继承额外的IDeclareReferencePrefabs接口,这里的转化相当于一个递归过程,因为可能会有Prefab的链式依赖的情况:
在这里插入图片描述



附录一些实际情况

BlobBuilderArray里填充数据不会直接改变BlobArray

我写了个简单的BlobArray的例子:

public class Test : MonoBehaviour
{
    // 数据必须是struct
    struct MarketData
    {
        public BlobArray<float> Prices;
    }

    // 返回BlobAssetReference<MarketData>对象
    BlobAssetReference<MarketData> CreateMarketData()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        ref MarketData marketData = ref builder.ConstructRoot<MarketData>();

        BlobBuilderArray<float> arrBuilder = builder.Allocate(ref marketData.Prices, 100);
        arrBuilder[0] = 5.0f;

        var result = builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);

        builder.Dispose();
        return result;
    }

Debug走到下图这里的适合,发现arrBuilder这个BlobBuilderArray里面是有数组信息的,实际marketData里的数组却是空的
在这里插入图片描述
这个CreateBlobAssetReference函数,会把刚刚创建的数据复制到result里,此时的result里就会真正的存储数组数据。

但我实际工作里碰到了这么个问题,我需要Cache住这个arrBuilder,然后异步的执行这个函数,直到完全填充好这个数组,最后调用CreateBlobAssetReference函数,难点在于:

  • BlobBuilderArray类型为public unsafe ref struct,貌似是不能直接存成Cache的

但是这个难点让我解决了,我发现这个类本质就是一个void*指针,和一个数组长度的interger,Unity还提供了这么个构造函数,意味着我只要存起来它的ptr和数组大小即可:
在这里插入图片描述

可以使用该类的函数获取对应指针:

		/// <summary>
  		/// Provides a pointer to the data stored in the array.
        /// </summary>
        /// <remarks>You can only call this function in an <see cref="Unsafe"/> context.</remarks>
        /// <returns>A pointer to the first element in the array.</returns>
        public void* GetUnsafePtr()
        {
            return m_data;
        }


当BlobAsset里有嵌套的数组时应该怎么Allocate

我发现这么写是不可以的:

public class Test : MonoBehaviour
{
    struct SubArray
    {
        public BlobArray<float> Prices;
    }

    // 数据必须是struct
    struct MarketData
    {
        public BlobArray<SubArray> arrs;
    }

    // 返回BlobAssetReference<MarketData>对象
    BlobAssetReference<MarketData> CreateMarketData()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        ref MarketData marketData = ref builder.ConstructRoot<MarketData>();

        BlobBuilderArray<SubArray> arrBuilder = builder.Allocate(ref marketData.arrs, 100);
        // 这么写会Runtime报错, 因为marketData.arrs为空数组, length为0
        BlobBuilderArray<float> subArrBuilder = builder.Allocate(ref marketData.arrs[0].Prices, 100);

        var result = builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);

        builder.Dispose();
        return result;
    }
}

需要借助这里的BlobBuilderArray来创建:

public class TestTuoba : MonoBehaviour
{
    struct SubArray
    {
        public BlobArray<float> Prices;
    }

    // 数据必须是struct
    struct MarketData
    {
        public BlobArray<SubArray> arrs;
    }

    // 返回BlobAssetReference<MarketData>对象
    BlobAssetReference<MarketData> CreateMarketData()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        ref MarketData marketData = ref builder.ConstructRoot<MarketData>();

        BlobBuilderArray<SubArray> arrBuilder = builder.Allocate(ref marketData.arrs, 100);

        // 借助BlobBuilderArray
        BlobBuilderArray<float> subArrBuilder = builder.Allocate(ref arrBuilder[0].Prices, 100);
        subArrBuilder[20] = 5f;

        //arrBuilder.GetUnsafePtr();
        var result = builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);

        builder.Dispose();

        // 发现arrrrr[20]显示为5
        var arrrrr = result.Value.arrs[0].Prices.ToArray();
        return result;
    }


奇怪的内存泄露

这里的Allocator.Temp,会让内存不断增长

private void OnClick()
{
        var builder = new BlobBuilder(Allocator.Temp);
        ref HobbyPool hobbyPool = ref builder.ConstructRoot<HobbyPool>();

        const int numHobbies = 800000000;
        BlobBuilderArray<Vector3> arrayBuilder = builder.Allocate(ref hobbyPool.Hobbies, numHobbies);

        builder.Dispose();
}

但如果我改成Persistent,就不会增长内存,奇怪了,可能跟没有调用CreateBlobAssetReference有关系吧:

var builder = new BlobBuilder(Allocator.Persistent);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity ECS记录 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • InvalidOperationException - 对象当前正在其他地方使用 - 红十字

    我有一个 C 桌面应用程序 其中我连续创建的一个线程从源 实际上是一台数码相机 获取图像并将其放在 GUI 中的面板 panel Image img 上 这必须是另一个线程 如它是控件的代码隐藏 该应用程序可以工作 但在某些机器上 我会在随
  • 未提供参数时如何指定 C# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 如何在c++中读取pcap文件来获取数据包信息?

    我想用 C 编写一个程序来读取 pcap 文件并获取数据包的信息 例如 len sourc ip flags 等 现在我找到了如下代码 我认为它会帮助我获取信息 但是我有一些疑问 首先我想知道应该将哪个库添加到我的程序中 然后什么是 pca
  • 如何将 protobuf-net 与不可变值类型一起使用?

    假设我有一个像这样的不可变值类型 Serializable DataContract public struct MyValueType ISerializable private readonly int x private readon
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 在 LINQ 中按 Id 连接多表和分组

    我想按categoryId显示列表产品的名称组 这是我的代码 我想要我的视图显示结果 Desktop PC HP Red PC Dell Yellow PC Asus Red SmartPhone Lumia 720 Blue 我的组模型
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • DbContext 和 ObjectContext 有什么区别

    From MSDN 表示工作单元和存储库模式的组合 使您能够查询数据库并将更改分组在一起 然后将这些更改作为一个单元写回存储 DbContext在概念上类似于ObjectContext 我虽然DbContext只处理与数据库的连接以及针对数
  • 如何检测表单的任何控件的变化?

    如何检测 C 中表单的任何控件的更改 由于我在一个表单上有许多控件 并且如果表单中的任何控件值发生更改 我需要禁用按钮 我正在寻找一些内置函数 事件处理程序 属性 并且不想为此创建自定义函数 不 我不知道任何时候都会触发任何事件any控制表
  • C# 中的合并运算符?

    我想我记得看到过类似的东西 三元运算符 http msdn microsoft com en us library ty67wk28 28VS 80 29 aspx在 C 中 它只有两部分 如果变量值不为空 则返回变量值 如果为空 则返回默
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 调用堆栈中的“外部代码”是什么意思?

    我在 Visual Studio 中调用一个方法 并尝试通过检查调用堆栈来调试它 其中一些行标记为 外部代码 这到底是什么意思 方法来自 dll已被处决 外部代码 意味着该dll没有可用的调试信息 你能做的就是在Call Stack窗口中单
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft com

随机推荐

  • React请求数据渲染页面

    1 使用react fetch数据发送请求 1 get方法 componentDidMount fetch url then res gt res json then json gt this setState list json 2 po
  • npm、pnpm、yarn的常用命令

    npm pnpm yarn的常用命令 文章目录 npm pnpm yarn的常用命令 一 常用命令 1 npm命令 2 pnpm命令 3 yarn命令 二 对比 一 常用命令 1 npm命令 npm init 初始化一个新的npm包 npm
  • 第12章 图形用户界面基础

    1 Swing和AWT的不同 AWT适合开发简单的图形用户界面 但不适合开发复杂的GUI项目 也容易发生于特定平台相关的故障 重量级组件 SWing更稳定 更通用 更灵活 不依赖于自己GUI 轻量级组件 SWing GUI组件类都以字母J为
  • EasyCHM编译的文件在点击节点时出现错误:确保Web地址//ieframe.dll/dnserrordiagoff.htm#正确

    EasyCHM编译后的文件打开时出现错误提示 解决方案 一 mht文件的文件名及路径中不能包含中文 二 修改节点的属性 检查路径是否正确
  • zookeeper

    1 zookeeper是什么 参考文献 Zookeeper可以干什么 zookeeper为分布式应用程序提供一致性协调服务 包括配置维护 域名服务 分布式锁 集群管理等 配置维护 同一个应用程序在不同服务器上的配置信息相同 将应用程序的配置
  • Android集成bilibili播放器以及弹幕

    考虑到开发直播和视频播放的必要性 网上了解到b站开源播放器 https github com bilibili ijkplayer 好用 集成下试试 运行后发现b站原生的只能播放没有其他选项 考虑到方便性 采用这个方案 https gith
  • Qt modbus使用详解

    不讲理论 只讲应用 看完这篇就能用起来 爽不爽 具体内容目录如下 如需请订阅专栏后观看 目录 一 Modbus协议通信过程 1 1 主机对从机写数据操作 0x06 1 2 主机对从机读数据操作 0x03 1 3 Modbus的CRC校验 二
  • 图的遍历(c语言)

    文章目录 图的遍历 种类 深度优先遍历 算法实现 广度优先遍历 算法实现 图的遍历 概念 图遍历是一种用于在图中搜索顶点的技术 图的遍历也用来决定在搜索过程中访问顶点的顺序 图的遍历可以在不创建循环的情况下找到要在搜索过程中使用的边 这意味
  • HJ92 在字符串中找出连续最长的数字串

    Powered by NEFU AB IN Link 文章目录 HJ92 在字符串中找出连续最长的数字串 题意 思路 代码 HJ92 在字符串中找出连续最长的数字串 题意 输入一个字符串 返回其最长的数字子串 以及其长度 若有多个最长的数字
  • java设计模式-单例模式

    package com hcmony singleton h3 单例模式 这种有并发问题 还有很多没有写 h3 p 单例模式 Singleton Pattern 是 Java 中最简单的设计模式之一 这种类型的设计模式属于创建型模式 它提供
  • fastapi与django异步的并发对比

    概述 据说fastapi是目前最快的异步框架 遂决定将其和django异步进行并发比较 先说结果 fastapi的异步可以使整体运行速度非常均衡 不会出现较大波动 但是django会出现大量的波动问题 部分访问速度很快 但是部分访问速度很慢
  • Android — 使用recyclerview+FlexboxLayoutManager实现Tag标签

    如图实现下面流式的tag标签 我们用recyclerview flexboxLayoutManager来实现 重点 FlexboxLayoutManager layoutManager new FlexboxLayoutManager th
  • 查看系统命令

    转载来自 https blog csdn net grgary article details 50975237 Linux下如何查看计算机的配置信息 cpu物理个数 几核 2016年03月24日 21 20 41 GJoker 阅读数 1
  • 【react】虚拟dom和真实dom

    关于虚拟dom 1 本质是Object类型的对象 一般对象 2 虚拟dom比较 轻 真实dom比较 重 因为虚拟dom是react内部在用 无需真实dom上那么多的属性 3 虚拟dom最终会被react转化为真实dom 呈现在页面上
  • Android_异常大全

    java lang NullPointerException 这个异常的解释是 程序遇上了空指针 简单地说就是调用了未经初始化的对象或者是不存在的对象 这个错误经常出现在创建图片 调用数组这些操作中 比如图片未经初始化 或者图片创建时的路径
  • C语言 程序 杨辉三角实现

    9 杨辉三角形 在屏幕上显示杨辉三角形 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 问题分析与算法设计 杨辉三角形中的数 正是 x y 的N次方幂展开式各项的系数 本题作为程序设计中具有代表性的题
  • 【推荐】SpringMVC与JSON数据返回及异常处理机制的使用

    艳艳耶 个人主页 个人专栏 推荐 Spring与Mybatis集成整合 生活的理想 为了不断更新自己 1 JSON 在SpringMVC中 JSON数据返回通常是通过使用 ResponseBody 注解将Java对象转换为JSON格式 并直
  • JDBC编程

    程序运行的时候 往往需要存取数据 现代应用程序最基本 也是最广泛的数据存储就是关系数据库 Java为关系数据库定义了一套标准的访问接口 JDBC Java Database Connectivity JDBC简介 在介绍JDBC之前 先简单
  • 软件测试岗:惨不忍睹的阿里三面,幸好做足了准备,已拿30koffer

    三面大概九十分钟 问的东西很全面 需要做充足准备 就是除了概念以外问的有点懵逼了 呜呜呜 回来之后把这些题目做了一个分类并整理出答案 强迫症的我 狂补知识 分为软件测试基础 Python自动化 性能测试 安全测试等 接下来分享一下我的这阿里
  • Unity ECS记录

    参考 What are Blob Assets 参考 Converting scene data to DOTS 参考 unity dots packages 参考 unity entities package documents 前言 我