aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/implementing_queryinterface
diff options
context:
space:
mode:
Diffstat (limited to 'files/zh-cn/implementing_queryinterface')
-rw-r--r--files/zh-cn/implementing_queryinterface/index.html172
1 files changed, 172 insertions, 0 deletions
diff --git a/files/zh-cn/implementing_queryinterface/index.html b/files/zh-cn/implementing_queryinterface/index.html
new file mode 100644
index 0000000000..6fe9761e22
--- /dev/null
+++ b/files/zh-cn/implementing_queryinterface/index.html
@@ -0,0 +1,172 @@
+---
+title: 实现QueryInterface接口
+slug: Implementing_QueryInterface
+translation_of: Mozilla/Implementing_QueryInterface
+---
+<p><font><font>本文档介绍正确地实现</font></font><code>QueryInterface()</code><font><font>的方式 </font></font><font><font>。</font></font></p>
+
+<h3 id="A_reference_implementation_of_QueryInterface" name="A_reference_implementation_of_QueryInterface">QueryInterface的参考实现</h3>
+
+<pre class="eval">NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+ {
+ NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+ <span class="comment">// It's a logic error, not a runtime error, to call me without any place to put my answer!</span>
+
+ <span class="comment">// ...but that won't matter when someone calls me wrongly in a non-debug build.</span>
+ if ( !aInstancePtr )
+ return NS_ERROR_NULL_POINTER;
+
+ nsISupports* foundInterface;
+
+ if ( aIID.Equals(nsCOMTypeInfo&lt;nsIX&gt;::GetIID()) )
+ foundInterface = NS_STATIC_CAST(nsIX*, this);
+ else if ( aIID.Equals(nsCOMTypeInfo&lt;nsIY&gt;::GetIID()) )
+ foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+ <span class="comment">// ...as many cases as needed...</span>
+
+ else if ( aIID.Equals(nsCOMTypeInfo&lt;nsISupports&gt;::GetIID()) )
+ foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this));
+ <span class="comment">// I (may) have multiple |nsISupports| in me,
+ // so first I cast to a specific base to avoid ambiguity</span>
+ else
+ foundInterface = 0;
+
+
+ nsresult status;
+ if ( !foundInterface )
+ status = NS_NOINTERFACE;
+ else
+ {
+ NS_ADDREF(foundInterface);
+ status = NS_OK;
+ }
+
+ *aInstancePtr = foundInterface;
+ return status;
+ }
+
+</pre>
+
+<h3 id="What.27s_So_Good_About_It.3F" name="What.27s_So_Good_About_It.3F">有什么好处呢?</h3>
+
+<p>代码优点:</p>
+
+<ul>
+ <li><font><font>简单明了。</font></font></li>
+ <li>好的。它有多个 <code>return</code>,但正如预期的那样,主要的 <code>return</code> 位于函数的末尾;并且附加的 <code>return</code>清晰而单独地位于函数的顶部。</li>
+ <li><font><font>它只有一个</font></font><code>AddRef</code><font><font>。</font></font></li>
+ <li><code>AddRef</code><font><font>是生成的接口,不是</font></font><code>this</code><font><font>,因此遵循 COM 正确的方式(在聚合中尤其重要)</font></font></li>
+ <li><font><font>它使用</font></font><code>nsCOMTypeInfo&lt;T&gt;::GetIID()</code><font><font>而不是</font></font><code>kTIID</code><font><font>节省全局声明和全局空间</font></font></li>
+ <li><font><font>它使用C的</font></font><code>NS_STATIC_CAST </code>定义<code>的</code><code>static_cast</code><font><font>,</font></font><font><font>当您无法真正获得所需的接口时,它将检测错误。</font></font></li>
+ <li><font><font>它避免了反复使用和分配给</font></font><code>*aInstancePtr</code><font><font>,这些会使编译器在优化时遇到麻烦。</font></font></li>
+ <li><code>*aInstancePtr</code><font><font>返回错误时</font><font>,将清除结果</font><font>。</font></font></li>
+ <li><font><font>与通常的</font></font><code>QueryInterface</code><font><font>实现相比,它生成的代码更少</font></font><font><font>。</font></font></li>
+ <li>它使用<code>NS_ASSERTION</code>测试错误输入,以便在调试构建中立即发现逻辑错误</li>
+</ul>
+
+<h3 id="Some_Alternatives" name="Some_Alternatives">一些替代方案</h3>
+
+<h4 id="The_NS_IMPL_QUERY_INTERFACE.5B012.5D_macros" name="The_NS_IMPL_QUERY_INTERFACE.5B012.5D_macros"><code>NS_IMPL_QUERY_INTERFACE</code>[<code>012</code>] 宏</h4>
+
+<p>除了<code>nsISupports</code>外,上面的示例还实现了两个{{ mediawiki.external('XP') }} COM接口。<code>NS_IMPL_QUERY_INTERFACE2</code>宏可以为您编写此函数(尽管我很难推荐宏),例如,</p>
+
+<pre class="eval">NS_IMPL_QUERY_INTERFACE2(nsMyImplementation, nsIX, nsIY)
+ <span class="comment">// implements |nsMyImplementation::QueryInterface| as above</span>
+
+NS_IMPL_QUERY_INTERFACE1(nsFoo, nsIFoo) <span class="comment">// |nsFoo::QueryInterface| provides |nsIFoo| and |nsISupports|</span>
+NS_IMPL_QUERY_INTERFACE0(nsBar) <span class="comment">// |nsBar::QueryInterface| can only provide an |nsISupports|</span>
+</pre>
+
+<p>同样,当仅实现一个附加接口时,可以使用宏<code>NS_IMPL_QUERY_INTERFACE1</code>;当仅实现<code>nsISupports</code>时,可以使用宏<code>NS_IMPL_QUERY_INTERFACE0</code> 。如果使用<code>NS_IMPL_ISUPPORTS*</code>宏,将为您调用这些宏,这些宏提供相应的<code>QueryInterface</code> 实现,以及<code>AddRef</code> 和<code>Release</code>。</p>
+
+<h4 id="Calling_an_inherited_QueryInterface" name="Calling_an_inherited_QueryInterface">调用继承的 <code>QueryInterface</code></h4>
+
+<p>有时,您只是将一个或两个新接口添加到已经支持许多其他接口的实现中。在这种情况下,在测试了所关心的特定<code>IID</code>之后,您可能希望调用底层实现。这节省了代码空间并降低了复杂性。下面的代码突出显示了这些差异。</p>
+
+<pre><code>class nsMyImplmentation : public nsBaseImplementation, public nsIX, public nsIY { ... };
+
+NS_IMETHODIMP
+nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
+ /*
+ I just add the interfaces |nsIX| and |nsIY|.
+ My base class |nsBaseImplementation| provides all the rest.
+ */
+ {
+ NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
+
+ if ( !aInstancePtr )
+ return NS_ERROR_NULL_POINTER;
+
+ nsISupports* foundInterface;
+
+ if ( aIID.Equals(nsCOMTypeInfo&lt;nsIX&gt;::GetIID()) )
+ foundInterface = NS_STATIC_CAST(nsIX*, this);
+ else if ( aIID.Equals(nsCOMTypeInfo&lt;nsIY&gt;::GetIID()) )
+ foundInterface = NS_STATIC_CAST(nsIY*, this);
+ // Note: Don't check for |nsISupports|; |nsBaseImplementation| will do that for me.
+ else
+ foundInterface = 0;
+
+
+ nsresult status;
+ if ( !foundInterface )
+ // OK, _I_ didn't find an interface. Maybe my base class can.
+ status = nsBaseImplementation::QueryInterface(aIID, &amp;foundInterface);
+ else
+ {
+ NS_ADDREF(foundInterface);
+ status = NS_OK;
+ }
+
+ *aInstancePtr = foundInterface;
+ return status;
+ }</code>
+</pre>
+
+<p>请注意,如果 base实现 的 <code>QueryInterface</code>找到了适当的接口,则你的<code>QueryInterface</code> 不能 <code>AddRef</code> 它。这反映在上面的代码中。<br>
+ <br>
+ 这种技术之所以有效,是因为 <code>nsBaseImplementation</code> 已经是一个可以单独使用的完整类。当你从几个完整的类派生时,这种技术不太合适;但如果您对顺序敏感,则仍然可以使用它,例如,</p>
+
+<pre><code>// ...
+ nsresult status;
+ if ( !foundInterface )
+ {
+ // OK, ask |nsBase1Imp| first, because I want _it_ to be the one true |nsISupports|.
+ status = nsBase1Imp::QueryInterface(aIID, &amp;foundInterface);
+
+ if ( !foundInterface )
+ status = nsBase2Imp::QueryInterface(aIID, &amp;foundInterface);
+
+ if ( !foundInterface )
+ status = nsBase3Imp::QueryInterface(aIID, &amp;foundInterface);
+ }
+ else
+ {
+ NS_ADDREF(foundInterface);
+ status = NS_OK;
+ }
+ // ...</code>
+</pre>
+
+<p>如果您的任何基类参与真正的聚合,那么要把正确的事情做好 即使不是不可能,也是很困难的。您将不能在聚合对象上获得对 <code>QueryInterface</code> 的调用,这可能会返回错误的接口。这也是一个需要特别避免 聚合和复杂的层次结构 的原因。</p>
+
+<h4 id="The_NS_GET_IID_macro" name="The_NS_GET_IID_macro"><code>NS_GET_IID</code> 宏</h4>
+
+<p>您可以使用·<code>NS_GET_IID</code> 宏,而不是键入完整的<code>GetIID </code>表达式。一般来说,我不赞成宏,除非在不同的情况下宏必须扩展为不同的文本,例如,不同的平台,调试与非调试,等等。在这种情况下,宏是必不可少的。在其他情况下,宏可能会帮助一些人,但通常会给其他人带来模糊的问题。它们总是使程序源更加脆弱。在这种情况下,宏只是为了方便起见,所以我不推荐它,但我确实提供了它作为替代。</p>
+
+<pre><code>// ...
+ if ( aIID.Equals(NS_GET_IID(nsIX)) )
+ foundInterface = NS_STATIC_CAST(nsIX*, this);
+ else if ( aIID.Equals(NS_GET_IID(nsIY)) )
+ foundInterface = NS_STATIC_CAST(nsIY*, this);
+
+ // ...as many cases as needed...
+
+ else if ( aIID.Equals(NS_GET_IID(nsISupports)) )
+ // ...</code></pre>
+
+<h3 id="Thanks" name="Thanks">Thanks</h3>
+
+<p>特别感谢 <a href="mailto:heikki@citec.fi">Heikki Toivonen</a>, <a href="mailto:waterson@netscape.com">Chris Waterson</a>和<a href="mailto:jband@netscape.com">John Bandhauer</a> 提供的宝贵反馈,这些反馈极大地改进了这里提供的实现</p>