不要拍我,但这是我第一次看到当地可写 的用法const(或者我可能只是太老了......):"WinAPI方式(来自TeamB的Peter Below)"
FullScreen: Boolean = False;然后看看当地的constFullScreen := not FullScreen;
起初我认为这是一个现代Delphi版本的新功能,但它也适用于我的D5.所以我的问题是:本地可写常量是否与声明全局可写常量完全相同?
例如
procedure TForm1.Button1Click(Sender: TObject);
Const
LocalConst: Boolean = False;
begin
LocalConst := not LocalConst;
if LocalConst then Beep;
end;
Run Code Online (Sandbox Code Playgroud)
像这段代码一样工作吗?:
Const
GlobalConst_Button2Click: Boolean = False;
procedure TForm1.Button2Click(Sender: TObject);
begin
GlobalConst_Button2Click := not GlobalConst_Button2Click;
if GlobalConst_Button2Click then Beep;
end;
Run Code Online (Sandbox Code Playgroud)
或者,LocalConst它是本地的方法,即静态?这个常量线程安全吗?
任何人都可以对这个问题有所了解吗?
Joh*_*ica 11
具有本地和全局类型常量的代码完全相同.
正如大卫已经声明的那样,全局静态变量(也称为类型常量)在整个程序中是可达的,而本地静态变量则不是.下面我将引用typed constants作为static variables,因为这是他们真的是.
但是,本地静态变量确实以与全局静态var完全相同的方式在内存中保留.只是编译器不允许您从例程外部访问本地var.
还要注意,如果你的本地静态var特别大(或者你有很多它们),它们将占用一块恒定的内存(即使我无法想象这可能是一个问题).
因为静态变量驻留在固定位置,所以它不是线程安全的.它有效地变成了所有线程实例之间的共享变量.
如果静态var不能在单个CPU周期中更改(即,如果它大于整数或者它是复杂类型),则两个线程可以同时更改变量的不同部分,通常会导致损坏.
它可以在一个循环中更改,例如布尔值或整数,而不是你永远不知道你所在的线程是最后一个改变它的线程还是另一个线程在大多数情况下会导致不可预测的结果.
简而言之,
在线程代码中使用静态变量是一个非常糟糕的主意,除非您确切知道自己在做什么.
一个例外可能是整数计数器,您只需递增并测试以查看是否发生了多于x次的执行.
静态变量通常不适合在线程之间传递消息.
如果您想在线程之间共享数据,最好使用它threadvar,
请参阅:http://en.wikipedia.org/wiki/Thread-local_storage
和:https://stackoverflow.com/search ?q = delphi + threread
最后
,很少有问题需要全局静态变量,最好避免它们,因为它们很危险.
本地静态变量在单线程代码中非常有用,可以跟踪例程的不同执行之间的状态.
由于竞争条件,它们在多线程代码中执行此操作毫无用处.
小智 5
我参加聚会有点晚了,但是我仍然想添加一些关于可写内容的信息。
首先,正如Johan和David所说,全局常数和局部常数在内存中没有区别。
对于那些对可写常量的使用感兴趣的人:我发现模仿“ Promise”功能以使函数“ Lazy”很有用。当然,delphi不支持Promises,因此这仅部分有效。
考虑一个函数来计算字符串中单词的数量:
function CountWords(Input: String):Integer;
var
Worker: TStringList;
begin
Worker := TStringList.Create;
Worker.DelimitedText := Input;
Result := Worker.Count;
Worker.Free;
end;
Run Code Online (Sandbox Code Playgroud)
现在想象一下它在我们的程序中被多次调用了。每次我们创建并释放TStringList对象时,都会做额外的工作。您当然可以通过创建全局变量Worker_CountWords来解决此问题,在程序启动时对其进行初始化并在函数中使用它,但请看一下以下内容:
function CountWords(Input: String):Integer;
{$J+} //Enable writable constants
const
Worker: TStringList = nil;
{$J-} //Disable writable constants
begin
if Worker = nil then
begin
Worker := TStringList.Create;
//Other Initialization code here
end;
Worker.DelimitedText := Input;
Result := Worker.Count;
end;
Run Code Online (Sandbox Code Playgroud)
该函数只会创建一次TStringList并在以后使用,但永远不会释放它(这里有点缺点)。但是对于可以在应用程序运行期间随时调用的函数来说,这是合适的。如果您愿意的话,这可以使您的代码看起来更整洁...现在,请注意-这实际上不是一个承诺,但是可以实现类似的结果。您也可以通过函数调用来做到这一点(我已经尝试过替换内存中的实际函数,这是一个很糟糕的主意,但是您可以创建一个const来保存指向函数的指针,该const在开始时保存指向初始化函数的指针,之后替换为实际的辅助函数,并且父函数只能调用保持在常量中的函数)。我现在想不出一个很好的例子,所以我让你自己解决这个问题。
另外,不需要{$ WRITABLECONST ON}来修改常量值,您还可以执行以下操作:
procedure DoSomeWork;
const
FirstCall : TDateTime = 0;
begin
if FirstCall = 0 then
PDateTime(@FirstCall)^ := Now;
Writeln(TimeToStr(FirstCall));
//some actual work here
end;
Run Code Online (Sandbox Code Playgroud)
同样的事情也适用const于函数中的参数,因为它们与var参数完全相同(为了避免花时间创建单独的变量而通过引用传递),唯一的区别是编译器不允许您正常更改这些值。
PS请小心使用const函数参数,因为您可以传递实际常量,例如,foo(12)并尝试对其进行修改,这可能会使事情变得混乱...