@Ming Zhou 在 https://community.intersystems.com/post/how-get-all-properties-defined-c 上问了一个很好的问题......而这个答案正好总结了为什么ObjectScript 是我最喜欢的语言。
当我第一次向别人介绍ObjectScript或IRIS时,我总是解释说,你可以写一个类,编译它,得到一个表,并从对象或关系的角度来处理你的数据--这是一种最自然的方式。
但无论哪种方式,它都只是一个薄薄的包装,包裹着被称为Globals的超级快速的内部数据结构,当你真的需要更快的速度时,你可以使用这些结构。
当我和比较爱较真的技术人员沟通时,我会告诉他们:ObjectScript允许各种炫酷的元编程,因为你可以用完全相同的方式与你刚刚写的类进行交互--从对象或关系的角度。或者在你需要额外的速度时直接使用超快的底层数据结构。
你可以在对这个问题的回答中看到答案: "我怎样才能得到一个类中的所有属性,包括继承的属性?"
这里有三种不同的方式来获得相同的答案。
Class DC.Demo.PropertyQuery Extends %Persistent
{
Property Foo As %String;
Property Bar As %Boolean;
/// Demonstrates all the ways to skin this particular cat
ClassMethod Run()
{
for method = "FromRelationship","WithQuery","AsQuicklyAsPossible" {
write !,method,":"
kill properties
do $classmethod($classname(),"GetProperties"_method,.properties)
do ..Print(.properties)
write !
}
}
ClassMethod Benchmark()
{
for method = "FromRelationship","WithQuery","AsQuicklyAsPossible" {
write !,method,":",!
set start = $zhorolog
set startGlobalRefs = $system.Process.GlobalReferences($job)
set startLines = $system.Process.LinesExecuted($job)
for i=1:1:1000 {
kill properties
do $classmethod($classname(),"GetProperties"_method,.properties)
}
set endLines = $system.Process.LinesExecuted($job)
set endGlobalRefs = $system.Process.GlobalReferences($job)
write "Elapsed time (1000x): ",($zhorolog-start)," seconds; ",(endGlobalRefs-startGlobalRefs)," global references; ",(endLines-startLines)," routine lines",!
}
}
/// Get properties using the properties relationship in %Dictionary.CompiledClass
ClassMethod GetPropertiesFromRelationship(Output properties)
{
// Minor problem: %OpenId and Properties.GetNext() are slow because they load more data than you strictly need.
// More global references = it takes longer.
set class = ##class(%Dictionary.CompiledClass).IDKEYOpen($classname(),,.sc)
$$$ThrowOnError(sc)
set key = ""
for {
set property = class.Properties.GetNext(.key)
quit:key=""
set properties(property.Name) = $listbuild(property.Type,property.Origin)
// Avoids consuming excess memory
do class.Properties.%UnSwizzleAt(key)
}
}
/// Get properties using a query against %Dictionary.CompiledProperty
ClassMethod GetPropertiesWithQuery(Output properties)
{
// Getting properties with SQL avoids the overhead of unnecessary references
set result = ##class(%SQL.Statement).%ExecDirect(,
"select Name,Type,Origin from %Dictionary.CompiledProperty where parent = ?",
$classname())
if result.%SQLCODE < 0 {
throw ##class(%Exception.SQL).CreateFromSQLCODE(result.%SQLCODE,result.%Message)
}
while result.%Next(.sc) {
$$$ThrowOnError(sc)
set properties(result.Name) = $listbuild(result.Type,result.Origin)
}
$$$ThrowOnError(sc)
}
/// Get properties using macros wrapping direct global references
ClassMethod GetPropertiesAsQuicklyAsPossible(Output properties)
{
// Getting properties via macro-wrapped direct global references is harder to read,
// but is the fastest way to do it.
set key = ""
set class = $classname()
for {
set key = $$$comMemberNext(class,$$$cCLASSproperty,key)
quit:key=""
set type = $$$comMemberKeyGet(class,$$$cCLASSproperty,key,$$$cPROPtype)
set origin = $$$comMemberKeyGet(class,$$$cCLASSproperty,key,$$$cPROPorigin)
set properties(key) = $listbuild(type,origin)
}
}
ClassMethod Print(ByRef properties)
{
set key = ""
for {
set key = $order(properties(key),1,data)
quit:key=""
set $listbuild(type,origin) = data
write !,"property: ",key,"; type: ",type,"; origin: ",origin
}
}
Storage Default
{
<Data name="PropertyQueryDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Foo</Value>
</Value>
<Value name="3">
<Value>Bar</Value>
</Value>
</Data>
<DataLocation>^DC.Demo.PropertyQueryD</DataLocation>
<DefaultData>PropertyQueryDefaultData</DefaultData>
<IdLocation>^DC.Demo.PropertyQueryD</IdLocation>
<IndexLocation>^DC.Demo.PropertyQueryI</IndexLocation>
<StreamLocation>^DC.Demo.PropertyQueryS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}