【问题标题】:Is there any performance benefit with "chaining" statements in .NET?.NET 中的“链接”语句是否有任何性能优势?
【发布时间】:2010-01-26 06:54:30
【问题描述】:

从表中检索查找代码值时,有些人会这样做...

Dim dtLookupCode As New LookupCodeDataTable()
Dim taLookupCode AS New LookupCodeTableAdapter()
Dim strDescription As String

dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
strDescription = dtLookupCode.Item(0).Meaning

...但是,我也看到过像这样“链式”完成的事情...

strDescription = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL").Item(0).Meaning

...首先绕过查找代码数据表,因为表适配器知道其结果集的结构是什么样的。

使用“链式”方法是否节省了创建数据表对象的开销,或者它是否有效地被创建以正确处理 .Item(0).Meaning 语句?

【问题讨论】:

  • 感谢 Chris 推荐“链接”的概念。我认为这更适合并避免“内联”的混淆。

标签: .net performance


【解决方案1】:

这两行将编译成相同的东西。我会选择你更容易阅读的那个。内联通常指的是a bit different

【讨论】:

    【解决方案2】:

    偏离“内联”部分,实际上,两组代码不会编译成相同的东西。问题出现在:

    Dim dtLookupCode As New LookupCodeDataTable()
    Dim taLookupCode AS New LookupCodeTableAdapter()
    

    在 VB 中,这将使用适当命名的引用创建新对象。其次是:

    dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
    

    我们立即将原来的 dtLookupCode 引用替换为一个新对象,这会产生要收集的垃圾(RAM 中无法访问的对象)。

    因此,在确切的原始场景中,所谓的“内联”技术在技术上具有更高的性能。 (但是,您不太可能在这个小示例中实际看到这种差异。)

    如果原始示例如下所示,则代码基本相同的地方是:

    Dim taLookupCode AS New LookupCodeTableAdapter
    Dim dtLookupCode As LookupCodeDataTable
    Dim strDescription As String
    
    dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
    strDescription = dtLookupCode.Item(0).Meaning
    

    在这个世界上,我们只有现有的引用,而不是创建垃圾对象。为了便于阅读,我稍微重新排序了这些语句,但要点是相同的。此外,您可以使用类似这样的方式轻松地对引用进行单行初始化,并且具有相同的基本思想:

    Dim taLookupCode AS New LookupCodeTableAdapter
    Dim dtLookupCode As LookupCodeDataTable = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
    Dim strDescription As String = dtLookupCode.Item(0).Meaning
    

    【讨论】:

      【解决方案3】:

      是的,不要说“内联”,因为这意味着其他语言中的特定内容。性能差异很可能为零或很小,这无关紧要,这只是一个偏好问题。您是想写在单独的语句中以使其更清晰,还是将其全部写在一行中以更快地输入?

      【讨论】:

        【解决方案4】:

        通常它只会降低代码的可读性。

        通常,当人们使用这种“内联”(即链接)时,他们会多次重新访问类的属性或字段,而不是只获取一次并存储在局部变量中。这通常是一个坏主意,因为人们通常不知道该字段或属性是如何返回的。比如可能每次都计算一次,也可能计算一次后私下存放在类中。

        这里有两个插图。第一个sn-p要避免:

        if (ConfigurationManager.AppSettings("ConnectionString") == null)
        {
            throw new MissingConfigSettingException("ConnectionString");
        }
        
        string connectionString = ConfigurationManager.AppSettings("ConnectionString");
        

        第二种更好:

        string connectionString = ConfigurationManager.AppSettings("ConnectionString")
        
        if (connectionString == null)
        {
            throw new MissingConfigSettingException("ConnectionString");
        }
        

        这里的问题是 AppSettings() 实际上必须在每次检索到值时对 AppSettings 集合进行拆箱:

        // Disassembled AppSettings member of ConfigurationManager 
        
        public static NameValueCollection AppSettings
        {
            get
            {
                object section = GetSection("appSettings");
        
                if ((section == null) || !(section is NameValueCollection))
                {
                    throw new
                        ConfigurationErrorsException(SR.GetString("Config_appsettings_declaration_invalid"));
                }
        
                return (NameValueCollection) section;
            }
        }
        

        【讨论】:

          【解决方案5】:

          如果您想查看中间状态并单步完成各个阶段,则调试后者会更加困难。

          我会考虑这里使用的屏幕空间数量的可读性,因为性能是一种洗涤。

          【讨论】:

            【解决方案6】:

            我称之为链接。

            你问错问题了。

            你需要问的是:哪个更具可读性?

            如果链接使代码更易于阅读和理解,那么就去做吧。

            但是,如果它混淆了,那么不要。

            不存在任何性能优化。不要优化代码,优化算法。

            因此,如果您要调用 Item(1) 和 Item(2),那么通过链接,您将一遍又一遍地创建相同的对象,这是一个糟糕的算法。

            在这种情况下,第一个选项更好,因为您不需要每次都重新创建适配器。

            【讨论】:

            • 当我在我的代码中使用查找表或其他“参考”类型表时,我通常只使用“链接”。如果我确实遇到了问题,我可以轻松地在调试器中停止该语句并在那里进行评估。对于我更关键的表,我确实使用了数据表对象并首先检索。谢谢!
            【解决方案7】:

            反对“链接”的一个原因是Law of Demeter,这表明您的代码在更改 LookupCodeDataTable 时很脆弱。

            你应该像这样添加一个函数:

            function getMeaning( lookupCode as LookupCodeDataTable)
             getMeaning=lookupCode.Item(0).Meaning
            end function
            

            然后这样称呼它:

            strDescription=getMeaning(taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL"))
            

            现在 getMeaning() 可以在许多其他地方调用,如果 LookupCodeDataTable 发生变化,那么您只需更改 getMeaning() 即可修复它。

            【讨论】:

              【解决方案8】:

              结构仍在创建中,只是没有参考。

              【讨论】:

                【解决方案9】:

                这个:

                dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
                strDescription = dtLookupCode.Item(0).Meaning
                

                还有这个:

                strDescription = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL").Item(0).Meaning
                

                完全等价。

                在第一个示例中,您有一个明确的临时引用 (dtLookupTable)。在第二个示例中,临时引用是隐式的。在幕后,编译器几乎肯定会为这两者创建相同的代码。即使它没有发出相同的代码,额外的临时引用也非常便宜。

                但是,我不确定这行是否:

                Dim dtLookupCode As New LookupCodeDataTable()
                

                是有效的。在我看来,这会创建一个新的LookupCodeDataTable,然后在您在后面的语句中覆盖变量时将其丢弃。我不会在 VB 中编程,但我希望这一行应该是:

                Dim dtLookupCode As LookupCodeDataTable
                

                参考很便宜(可能是免费的),但构建一个额外的查找表可能不是。

                【讨论】:

                • 在 VB 中,您必须在使用表适配器之前声明新的数据表对象,否则会出现空对象异常。有点奇怪,但这就是它滚动的方式。 8^D
                • 不过,您没有在示例中使用数据表对象。您将立即覆盖引用。约翰鲁迪指出了同样的事情。
                【解决方案10】:

                除非您需要通过以下方式引用返回的对象,否则它是相同的 taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL") 或 Item(0) 多次。否则,您不知道此函数的运行时是 log(n) 还是 n,所以最好的选择是,我会为其分配一个引用。

                【讨论】:

                  【解决方案11】:

                  除了可维护性之外,还有一个避免链接的原因:错误检查。

                  是的,您可以将整个事情包装在 try/catch 中,并捕获链的任何部分可以抛出的每个异常。

                  但是,如果您想在不使用 try/catch 的情况下在调用之间验证结果,则必须将它们分开。例如:

                  • GetDataByCodeAndValue 返回 null 时会发生什么?
                  • 如果它返回一个空列表怎么办?

                  如果你正在链接,你不能在没有 try/catch 的情况下检查这些值。

                  【讨论】:

                    猜你喜欢
                    • 2011-06-11
                    • 2017-03-04
                    • 2016-03-14
                    • 2011-12-18
                    • 1970-01-01
                    • 2015-10-18
                    • 1970-01-01
                    • 2023-03-04
                    • 2015-01-30
                    相关资源
                    最近更新 更多