如何摆脱虚拟表?密封类

时间:2017-02-28 10:09:08

标签: c# oop inheritance vtable sealed

据我所知,让sealed课程在VTable中找不到,或者我错了?如果我创建一个类sealed,这是否意味着类层次结构中的所有虚方法也都标记为public class A { protected virtual void M() { ........ } protected virtual void O() { ........ } } public sealed class B : A { // I guess I can make this private for sealed class private override void M() { ........ } // Is this method automatically sealed? In the meaning that it doesn't have to look in VTable and can be called directly? // Also what about O() can it be called directly too, without VTable? }

例如:

JSONArray posts = response.optJSONArray(ServerResponseStorage.s_szDEAL_ARRAY);// GETTING DEAL LIST
                    for (int i = 0; i < posts.length(); i++) {
                        try {
                            JSONObject post = posts.getJSONObject(i);// GETTING DEAL AT POSITION AT I
                            m_VcomData = new CVcomStorage();// object create of DealAppdatastorage
                            m_VcomData.setmDealTitle(post.getString(ServerResponseStorage.s_szDEAL_NAME));//getting deal name
                            m_VcomData.setmDealCode(post.getString(ServerResponseStorage.s_szDEAL_CODE));// getting deal code
                            m_VcomData.setmDealValue(post.getString(ServerResponseStorage.s_szDEAL_VAlUE));
                            m_VcomData.setmDescription(post.getString(ServerResponseStorage.s_szDEAL_DETAILS));
                            m_VcomData.setmDealActionUrl(post.getString(ServerResponseStorage.s_szDEAL_ACTION_URL));
                            String logo = post.getString(ServerResponseStorage.s_szDEAL_LOGO);
                            m_VcomData.setmIcon(imgPath + logo);
                            if (BuildConfig.klogInfo)
                                Log.d(m_kTAG, "Logo Path::" + item.getS_szicon());
                            if (BuildConfig.kMonkeyInfo)
                                Log.i("Monkey", "Logo Path::" + item.getS_szicon());
                            if (!s_VcomDataSet.contains(item)) {
                                s_VcomDataSet.add(m_VcomData);
                                m_VcomAdpter.notifyDataSetChanged();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                    m_VcomAdpter = new CVcomAdapter(HomeScreenActivity.this, s_VcomDataSet);
                    mVcomListview.setAdapter(m_VcomAdpter);

4 个答案:

答案 0 :(得分:2)

  

&#34;我想我可以将这个私密用于密封课程&#34;

您无法在继承层次结构中更改访问修饰符。这意味着如果方法在基类中为public,则无法在派生类中使其成为privateinternalprotected。只有在将方法声明为new时才能更改修饰符:

private new void M() { ........ }
  

据我所知,制作类密封摆脱了VTable中的查找   我错了吗?

密封类是层次结构中的最后一个,因为您无法继承它。如果此sealed类覆盖基类中的某些方法,则虚拟表可与sealed类一起使用。

  

如果我让一个类密封,这意味着所有的虚拟方法   类层次结构也标记为密封?

您可以看到IL代码:

.method family hidebysig virtual            // method is not marked as sealed
    instance void M () cil managed 
{        
    .maxstack 8

    IL_0000: nop 
    IL_0001: ret value
}  

方法未标记为sealed。即使您明确将此方法标记为sealed,您也将获得相同的IL代码。

此外,没有理由在sealed课程中将方法标记为sealed。如果课程为sealed,您无法继承,那么您就无法继承它的方法。

关于虚拟表 - 如果方法覆盖并且您从虚拟表中删除它,则永远不能在继承层次结构中使用它,因此,没有理由重写方法并且永远不会在继承层次结构中使用它。

答案 1 :(得分:2)

首先要注意的是,C#通常会对引用类型上的任何实例方法进行虚拟调用,即使该方法不是虚拟的。这是因为有一个C#规则,在一个不是.NET规则的空引用上调用方法是非法的(如果你在一个空引用上调用一个方法而且该方法没有&#,则在原始CIL中) 39; t本身访问一个字段或虚方法,它工作正常)并使用callvirt是一种廉价的方法来强制执行该规则。

对于非虚拟实例调用,C#将生成call而不是callvirt,在某些情况下,显然引用不是空的。特别是对于obj?.SomeMethod(),因为?.表示已经进行了空检查,如果SomeMethod()不是虚拟的,则会将其编译为call。这只发生在?.,因为编译?.的代码可以进行检查,而if (obj != null){obj.SomeMethod();}不会发生这种情况,因为编译.的代码不会发生。{1}}我知道它正在进行空检查。 所涉及的逻辑非常本地化

在CIL级别可以跳过虚拟表查找虚拟方法。这就是base调用工作的方式;编译为call基于方法的实现而不是callvirt。通过构造obj?.SomeMethod()中的扩展,其中SomeMethod是虚拟的并且是密封的(无论是单独的还是因为obj的类型被密封),理论上可以将其编译为{{1}到派生类型最多的实现。虽然声明类型和密封类型之间的层次结构中的类添加或删除覆盖,但仍需要进行一些额外的检查以确保它仍能正常工作。它需要一些全局的层次结构知识(并保证知识不会发生变化,这意味着当前正在编译的程序集中的所有类型)才能使优化变得安全。收益很小。并且在大多数情况下仍然无法使用,原因与call在大多数情况下甚至在非虚拟呼叫中使用的原因相同。

我不认为callvirt会影响编译器如何生成呼叫,而且它在大多数情况下肯定不会影响它。

抖动可以自由地应用更多的知识,但是如果有的话,差异会非常小。我当然建议您将自己知道的课程标记为sealed,并且如果抖动使用了那些,那么这很好,但我推荐的主要原因它不是表现而是正确。如果你某个地方试图覆盖你标记为sealed的类,那么要么A.你刚刚改变了设计,并且知道你必须删除sealed(。 5秒钟可以删除它)或B.你已经在一个地方做过一些事情,你确信你不会在另一个地方。暂时重新考虑是件好事。

  

如果我使类密封,这是否意味着类层次结构中的所有虚方法也都标记为密封?

它们被认为是密封的,就像明确标记为密封一样。

答案 2 :(得分:0)

密封意味着您无法继承或覆盖它。如果你不能继承B类,你就无法重写方法M. 至于查找,我不认为你保存它。在你的示例中,调用方法O必须从类A中查找方法并调用它。这是通过vtable进行的。

答案 3 :(得分:0)

使类或方法密封将无效,对B.M()的任何调用都将是虚拟的。

如果我不得不打赌为什么会这样,我会说它因为M()A中声明,覆盖它不会使该方法“属于”B

如果您检查生成的代码的IL,您将看到相关指令为callvirt instance string namespace.A::M(),因此即使密封B,呼叫也必须是虚拟的。