.NET中的"链接"语句对性能有什么好处?
从表中检索查询代码值时,有些人会这样做...
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的情况下检查这些值。