.NET中的"链接"语句对性能有什么好处?

时间:2020-03-06 14:27:46  来源:igfitidea点击:

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

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语句而有效地创建了它?

解决方案

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

这两行将编译为同一件事。我会选择哪一个更容易为我们阅读。内联通常指的是有些不同。

该结构仍处于创建状态,我们只是没有参考。

通常,这只会使代码的可读性降低。

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

这是两个插图。应避免使用第一个代码段:

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;
    }
}

如果要查看中间状态,并单步调试各个阶段,则调试后者将变得更加困难。

为了提高可读性,我会在这里使用屏幕空间,因为性能是一种洗礼。

实际上,从其中的"内联"部分出发,这两套代码不会编译成同一件事。问题来了:

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

这:

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

该引用很便宜(可能是免费的),但构造一个额外的查找表可能并非如此。

我称之为连锁。

我们在问错问题。

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

如果链接使代码更容易阅读和理解,则比继续做起来要容易。

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

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

因此,如果要调用Item(1)和Item(2),则通过链接,我们将一次又一次地创建相同的对象,这是一个不好的算法。

在这种情况下,第一种选择更好,因为我们不必每次都重新创建适配器。

反对"链接"的一个原因是Demeter定律,它表明面对LookupCodeDataTable的更改,代码很脆弱。

我们应该添加如下功能:

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

并这样称呼它:

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

现在可以在许多其他地方调用getMeaning(),并且如果LookupCodeDataTable发生更改,则只需更改getMeaning()即可对其进行修复。

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

除了可维护性,这是避免链接的另一个原因:错误检查。

是的,我们可以将整个内容包装在try / catch中,并捕获链中任何部分可能引发的异常。

但是,如果要在两次调用之间验证结果而又不尝试/不捕获,则必须将事情分开。例如:

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

如果要链接,则无法在没有try / catch的情况下检查这些值。