Move() 从动态字符串数组中插入/删除项目
使用 System.Move() 从字符串数组中插入/删除项目并不像从其他简单数据类型数组中插入/删除项目那么容易。问题是...字符串在 Delphi 中是引用计数的。在引用计数数据类型上使用 Move() 需要对内部编译器行为有更深入的了解。
这里有人可以解释我实现这一目标所需的步骤,或者更好地使用一些代码片段,或者指导我在互联网上找到一个很好的参考吗?
哦,请不要告诉我使用“懒惰但缓慢的方式”,即for循环,我知道。
Using System.Move() to insert/delete item(s) from an array of string is not as easy as insert/delete it from other array of simple data types. The problem is ... string is reference counted in Delphi. Using Move() on reference-counted data types needs deeper knowledge on internal compiler behaviour.
Can someone here explain the needed steps for me to achieve that, or better with some snippet codes, or direct me to a good reference on the internet?
Oh, Please don't tell me to use the "lazy-but-slow way", that is, for loop, I know that.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我之前演示过如何从动态数组中删除项目:
在那篇文章中,我从以下代码开始:
使用该代码不会出错。使用您想要的
X
任何值;根据您的情况,将其替换为string
。如果您想要更高级地使用Move
,那么也有办法做到这一点。由于
X
是string
,因此Finalize
调用相当于将空字符串分配给该数组元素。不过,我在此代码中使用了 Finalize,因为它适用于所有数组元素类型,甚至包括记录、接口、字符串和其他数组的类型。对于插入,您只需将内容向相反方向移动:
当您要执行语言范围之外的操作(例如使用非类型安全的
Move<)时,请使用
Finalize
/code> 覆盖编译器管理类型变量的过程。当您重新输入语言的定义部分时,请使用Initialize
。 (该语言定义了当数组使用SetLength
增大或缩小时会发生什么,但它没有定义如何在不使用字符串赋值语句的情况下复制或删除字符串。)I've demonstrated how to delete items from a dynamic array before:
In that article, I start with the following code:
You cannot go wrong with that code. Use whatever value for
X
you want; in your case, replace it withstring
. If you want to get fancier and useMove
, then there's way to do that, too.Since
X
isstring
, theFinalize
call is equivalent to assigning the empty string to that array element. I useFinalize
in this code, though, because it will work for all array-element types, even types that include records, interfaces, strings, and other arrays.For inserting, you just shift things the opposite direction:
Use
Finalize
when you're about to do something that's outside the bounds of the language, such as using the non-type-safeMove
procedure to overwrite a variable of a compiler-managed type. UseInitialize
when you're re-entering the defined part of the language. (The language defines what happens when an array grows or shrinks withSetLength
, but it doesn't define how to copy or delete strings without using a string-assignment statement.)您没有说明将数组元素保持相同的顺序对您来说是否重要。
如果顺序不相关,您可以非常非常快像这样:
对列表进行排序
如果您有一个巨大的列表需要用户修改,您可以使用与上面类似的方法(打破列表顺序)。当用户完成编辑(多次删除后)时,您会向其显示一个名为“排序列表”的按钮。现在他可以执行冗长的(排序)操作。
当然,我上面假设您的列表可以按某个参数排序。
自动对列表进行排序
另一种方法是自动化排序过程。当用户从列表中删除内容时,启动计时器。如果用户不断删除项目,请继续重置计时器。当计时器成功触发事件时,进行排序,停止计时器。
You don't state if it is important for you to keep the array elements in the same order or not.
If the order is not relevant, you can so something really really fast like this:
Sorting the list
If you have a HUGE list that needs to be modified by the user, you can use methods similar to the one above (break the list order). When the user its done editing (after multiple deletes), you present it with a button called "Sort list". Now he can do the lengthy (sort) operation.
Of course, I assume above that your list can be sorted by a certain parameter.
Sorting the list automatically
An alternative is to automate the sorting process. When the user deleted stuff from the list, start a timer. Keep resetting the timer if the user keeps deleting items. When the timer manages to trigger an event, do the sorting, stop the timer.
要插入字符串,只需将字符串(惰性方式)添加到数组(指针数组)的末尾,然后使用
Move
更改该数组元素的顺序(指针)。To insert a string, simply add a string (the lazy way) to the end of the array (which is an array of pointers), and then use
Move
to change the order of the elements of this array (of pointers).如果我想将一个字符串插入到字符串列表的中间,我会使用 TStringList.Insert。 (它使用 System.Move 快速完成此操作。)
您使用数组而不是 TStringList 的任何特殊原因吗?
If I wanted to insert a string into the middle of a list of strings, I'd use TStringList.Insert. (It does it quickly using System.Move.)
Any particular reason why you're using an array instead of a TStringList?
在搞乱它之前,先调用 UniqueString() 。
http://docwiki.embarcadero.com/VCL/en/System.UniqueString
然后你就有了一个带有单个引用的字符串。
很有可能这也是删除和插入所做的,我怀疑你会更快。
Call UniqueString() on it, before messing with it.
http://docwiki.embarcadero.com/VCL/en/System.UniqueString
Then you have a string with a single reference.
Fat chance that that is what delete and insert do too, and I doubt you'll be faster.
只是想为将来来到这里的任何人添加这一点。
修改 Rob 的代码后,我想出了这种使用较新的
TArray
类型结构的方法。可以对插入件执行类似的操作。
(并不是想将此标记为答案,只是想提供一个示例,该示例太长,无法放入对 Rob 优秀答案的评论中。)
(已修复以解决下面 Rob 的评论。)
Just wanting to add this for any people that come here in the future.
Modifying Rob's code, I came up with this way of doing it that uses the newer
TArray<T>
type constructions.A similar thing can be done for the insert.
(Not looking for this to get marked as the answer, just looking to provide an example that was too long to fit in the comments on Rob's excellent answer.)
(Fixed to address Rob's comments below.)
Move() 可以很好地处理引用计数类型,如字符串或接口,并且实际上在 Delphi 的数组和列表内部使用。但是,现在,在一般情况下,由于托管记录功能,Move() 不再有效。
Move() works fine with reference counted types like strings or interfaces, and actually used internally in Delphi's arrays and lists. But, now, in general case, Move() is no longer valid because of managed records feature.
如果您使用 System.Move 将项目放入字符串数组中,您应该知道 Move 之前的字符串(现在被覆盖)的引用计数要么为 -1(对于常量字符串),要么为 > 。 0 表示变量字符串。不应更改常量字符串,但应相应地处理变量字符串:您应该手动降低它们的引用计数(在它们被覆盖之前!)。要做到这一点,你应该尝试这样的事情:
但是如果引用计数达到零,你还应该最终确定关联的内存 - 如果你让它工作,Delphi 本身会做得更好,它是字符串的编译器魔法。哦,还有:如果您复制的字符串与您写入的字符串来自同一个数组,则所需的管理会变得非常麻烦,而且很快!
因此,如果可以以某种方式避免所有这些手动内务处理,我建议让 Delphi 自行处理。
If you use System.Move to put items into an array of string, you should be aware that the strings that where there before the Move (and now overwritten), had a reference count of either -1 for constant strings, or > 0 for variable strings. Constant strings should not be altered, but variable strings should be treated accordingly: You should manually lower their reference-count (before they're overwritten!). To do that, you should try something like this:
But if the reference-count reached zero, you should also finalize the associated memory - something Delphi itself does a whole lot better if you let it work it's compiler-magic for strings. Oh, and also : if the strings you're copying come from the same array as your writing into, the needed administration becomes very cumbersome, very quickly!
So if it's in some way possible to avoid all this manual housekeeping, I would advise to let Delphi handle it itself.