diff options
Diffstat (limited to 'files/zh-cn/implementing_queryinterface')
-rw-r--r-- | files/zh-cn/implementing_queryinterface/index.html | 172 |
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<nsIX>::GetIID()) ) + foundInterface = NS_STATIC_CAST(nsIX*, this); + else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID()) ) + foundInterface = NS_STATIC_CAST(nsIY*, this); + + <span class="comment">// ...as many cases as needed...</span> + + else if ( aIID.Equals(nsCOMTypeInfo<nsISupports>::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<T>::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<nsIX>::GetIID()) ) + foundInterface = NS_STATIC_CAST(nsIX*, this); + else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::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, &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, &foundInterface); + + if ( !foundInterface ) + status = nsBase2Imp::QueryInterface(aIID, &foundInterface); + + if ( !foundInterface ) + status = nsBase3Imp::QueryInterface(aIID, &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> |