vb.net - DirectCast 对性能和后期/早期绑定(bind)有什么影响?

标签 vb.net performance directcast

我一直以为DirectCast()在性能和内存方面相当便宜,并且基本上将其视为帮助我使用 IntelliSense 的一种方式,例如在事件处理程序中:

Public Sub myObject_EventHandler(sender As Object, e As System.EventArgs)
    'Explicit casting with DirectCast
    Dim myObject As myClass = DirectCast(sender, myClass)

    myObject.MyProperty = "myValue"
End Sub

我认为这显然对我作为开发人员来说更好,而且对于编译的代码和产生的性能也更好,因为它启用了“早期绑定(bind)”而不是......
Public Sub myObject_EventHandler(sender As Object, e As System.EventArgs)
    'No casting at all (late binding)
    myObject.MyProperty = "myValue"
End Sub

...如果我正确理解了条款,它也可以编译和运行,但使用“后期绑定(bind)”。即假设 sender实际上是 myClass目的。

关于性能、后​​期/早期绑定(bind)或其他任何内容,上面的第一个片段和下面的片段有什么区别:
Public Sub myObject_EventHandler(sender As Object, e As System.EventArgs)
    'Implicit casting via variable declaration
    Dim myObject As myClass = sender

    myObject.MyProperty = "myValue"
End Sub

是显式的 DirectCast()在编译器优化代码后调用有用/有害还是没有区别?

最佳答案

TL;DR 版本:
使用DirectCast()而不是后期绑定(bind)或反射以获得更好的运行时性能。

这个问题对我来说非常有趣。我用 DirectCast()对我编写的几乎所有应用程序都定期进行。我一直使用它,因为它使 IntelliSense 工作,如果我不使用 Option Strict On那么我会在编译时出错。每隔一段时间我都会使用Option Strict Off如果我赶时间并且正在测试设计概念,或者如果我急于为一次性问题提供快速而肮脏的解决方案。我从来没有考虑过使用它时的性能影响,因为我从来不用担心我写的东西的运行时性能。

想到这个问题让我有点好奇,所以我决定自己测试一下,看看有什么不同。我在 Visual Studio 中创建了一个新的控制台应用程序并开始工作。以下是该应用程序的完整源代码。如果您想自己查看结果,您应该可以直接复制/粘贴:

Option Strict Off
Option Explicit On
Imports System.Diagnostics
Imports System.Reflection
Imports System.IO
Imports System.Text


Module Module1
    Const loopCntr As Int32 = 1000000
    Const iterationCntr As Int32 = 5
    Const resultPath As String = "C:\StackOverflow\DirectCastResults.txt"

    Sub Main()
        Dim objDirectCast As New MyObject("objDirectCast")
        Dim objImplicitCasting As New MyObject("objImplicitCasting")
        Dim objLateBound As New MyObject("objLateBound")
        Dim objReflection As New MyObject("objReflection")
        Dim objInvokeMember As New MyObject("objInvokeMember")
        Dim sbElapsed As New StringBuilder : sbElapsed.Append("Running ").Append(iterationCntr).Append(" iterations for ").Append(loopCntr).AppendLine(" loops.")
        Dim sbAverage As New StringBuilder : sbAverage.AppendLine()

        AddHandler objDirectCast.ValueSet, AddressOf SetObjectDirectCast
        AddHandler objImplicitCasting.ValueSet, AddressOf SetObjectImplictCasting
        AddHandler objLateBound.ValueSet, AddressOf SetObjectLateBound
        AddHandler objReflection.ValueSet, AddressOf SetObjectReflection
        AddHandler objInvokeMember.ValueSet, AddressOf SetObjectInvokeMember

        For Each myObj As MyObject In {objDirectCast, objImplicitCasting, objLateBound, objReflection, objInvokeMember}
            Dim resultlist As New List(Of TimeSpan)
            sbElapsed.AppendLine().Append("Time (seconds) elapsed for ").Append(myObj.Name).Append(" is: ")
            For i = 1 To iterationCntr
                Dim stpWatch As New Stopwatch
                stpWatch.Start()
                For _i = 0 To loopCntr
                    myObj.SetValue(_i)
                Next
                stpWatch.Stop()
                sbElapsed.Append(stpWatch.Elapsed.TotalSeconds.ToString()).Append(", ")
                resultlist.Add(stpWatch.Elapsed)
                Console.WriteLine(myObj.Name & " is done.")
            Next

            Dim totalTicks As Long = 0L
            resultlist.ForEach(Sub(x As TimeSpan) totalTicks += x.Ticks)
            Dim averageTimeSpan As New TimeSpan(totalTicks / CLng(resultlist.Count))
            sbAverage.Append("Average elapsed time for ").Append(myObj.Name).Append(" is: ").AppendLine(averageTimeSpan.ToString)
        Next

        Using strWriter As New StreamWriter(File.Open(resultPath, FileMode.Create, FileAccess.Write))
            strWriter.WriteLine(sbElapsed.ToString)
            strWriter.WriteLine(sbAverage.ToString)
        End Using
    End Sub

    Sub SetObjectDirectCast(sender As Object, newValue As Int32)
        Dim myObj As MyObject = DirectCast(sender, MyObject)
        myObj.MyProperty = newValue
    End Sub

    Sub SetObjectImplictCasting(sender As Object, newValue As Int32)
        Dim myObj As MyObject = sender
        myObj.MyProperty = newValue
    End Sub

    Sub SetObjectLateBound(sender As Object, newValue As Int32)
        sender.MyProperty = newValue
    End Sub

    Sub SetObjectReflection(sender As Object, newValue As Int32)
        Dim pi As PropertyInfo = sender.GetType().GetProperty("MyProperty", BindingFlags.Public + BindingFlags.Instance)
        pi.SetValue(sender, newValue, Nothing)
    End Sub

    Sub SetObjectInvokeMember(sender As Object, newValue As Int32)
        sender.GetType().InvokeMember("MyProperty", BindingFlags.Instance + BindingFlags.Public + BindingFlags.SetProperty, Type.DefaultBinder, sender, {newValue})
    End Sub
End Module

Public Class MyObject
    Private _MyProperty As Int32 = 0
    Public Event ValueSet(sender As Object, newValue As Int32)
    Public Property Name As String

    Public Property MyProperty As Int32
        Get
            Return _MyProperty
        End Get
        Set(value As Int32)
            _MyProperty = value
        End Set
    End Property

    Public Sub New(objName As String)
        Me.Name = objName
    End Sub

    Public Sub SetValue(newvalue As Int32)
        RaiseEvent ValueSet(Me, newvalue)
    End Sub

End Class

我尝试在 Release 中运行应用程序配置以及在没有附加 Visual Studio 调试器的情况下运行发布配置。以下是输出结果:

使用 Visual Studio 调试器发布:

Running 5 iterations for 1000000 loops.

Time (seconds) elapsed for objDirectCast is: 0.0214367, 0.0155618, 0.015561, 0.015544, 0.015542, 
Time (seconds) elapsed for objImplicitCasting is: 0.014661, 0.0148947, 0.015051, 0.0149164, 0.0152732, 
Time (seconds) elapsed for objLateBound is: 4.2572548, 4.2073932, 4.3517058, 4.480232, 4.4216707, 
Time (seconds) elapsed for objReflection is: 0.3900658, 0.3833916, 0.3938861, 0.3875427, 0.4558457, 
Time (seconds) elapsed for objInvokeMember is: 1.523336, 1.1675438, 1.1519875, 1.1698862, 1.2878384, 

Average elapsed time for objDirectCast is: 00:00:00.0167291
Average elapsed time for objImplicitCasting is: 00:00:00.0149593
Average elapsed time for objLateBound is: 00:00:04.3436513
Average elapsed time for objReflection is: 00:00:00.4021464
Average elapsed time for objInvokeMember is: 00:00:01.2601184

不使用 Visual Studio 调试器发布:

Running 5 iterations for 1000000 loops.

Time (seconds) elapsed for objDirectCast is: 0.0073776, 0.0055385, 0.0058196, 0.0059637, 0.0057557, 
Time (seconds) elapsed for objImplicitCasting is: 0.0060359, 0.0056653, 0.0065522, 0.0063639, 0.0057324, 
Time (seconds) elapsed for objLateBound is: 4.4858827, 4.1643164, 4.2380467, 4.1217441, 4.1270739, 
Time (seconds) elapsed for objReflection is: 0.3828591, 0.3790779, 0.3849563, 0.3852133, 0.3847144, 
Time (seconds) elapsed for objInvokeMember is: 1.0869766, 1.0808392, 1.0881596, 1.1139259, 1.0811786, 

Average elapsed time for objDirectCast is: 00:00:00.0060910
Average elapsed time for objImplicitCasting is: 00:00:00.0060699
Average elapsed time for objLateBound is: 00:00:04.2274128
Average elapsed time for objReflection is: 00:00:00.3833642
Average elapsed time for objInvokeMember is: 00:00:01.0902160

查看这些结果看起来好像使用 DirectCast()与添加强制转换的编译器相比,几乎没有性能损失。但是,当依赖对象进行后期绑定(bind)时,会出现 巨大的性能下降,执行速度大大减慢。使用 System.Reflection 时,与直接转换对象相比有轻微的减速。我认为获得 PropertyInfo 是不寻常的。比使用 .InvokeMember() 快得多方法。

结论:
尽可能使用 DirectCast()或直接转换对象。应该只在需要时才使用反射。仅将后期绑定(bind)的元素用作最后的手段。不过说实话,如果你只是在这里或那里修改一个对象几次,它可能在宏伟的计划中并不重要。

相反,您应该更关心这些方法如何失败以及如何防止它们这样做。例如,在 SetObjectInvokeMember()方法,如果 sender对象没有 MyProperty属性,那么那段代码就会抛出异常。在 SetObjectReflection()方法,返回的属性信息可能是 nothing这将导致 NullReferenceException .

关于vb.net - DirectCast 对性能和后期/早期绑定(bind)有什么影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29749832/

相关文章:

c# - 在表中插入值的 asp.net 查询

asp.net - 如何在响应重定向之前调用 javascript

vb.net - VB.NET 中 DirectCast() 和 CType() 之间的区别

sql-server - LINQ to SQL 和 DBML 文件——多数据库开发

c# - .NET Singleton 长时间运行的秒表实例

c# - 为什么这个Linq表达式比另一个表达式慢很多?

c# - C++ 比 C# 快多少?

c# - C#'s equivalent to VB.NET' s 直播

asp.net - 根据存储过程中的非 bool 值检查 GridView 中的复选框

c# - 如何使用用户键入的关联显示文本获取 ComboBox 的值成员?