TL; DR versione: Uso DirectCast()
invece di associazione tardiva o di riflessione per migliorare le prestazioni di esecuzione.
Questa domanda è stata molto intrigante per me. Io uso DirectCast()
su base molto regolare per quasi tutte le applicazioni che ho scritto. L'ho sempre usato perché rende IntelliSense funzionante e se non lo faccio con Option Strict On
allora ricevo errori durante la compilazione. Ogni tanto uso Option Strict Off
se ho fretta e sto testando un concetto di design o se ho fretta per una soluzione rapida e sporca a un problema una tantum. Non ho mai pensato un secondo agli effetti delle prestazioni nell'usarlo perché non ho mai dovuto preoccuparmi delle prestazioni di runtime con le cose che scrivo.
Pensare a questa domanda mi ha reso un po 'curioso, quindi ho deciso di provarlo e vedere le differenze da solo. Ho creato una nuova applicazione Console in Visual Studio e sono andato a lavorare. Di seguito è riportato l'intero codice sorgente per l'applicazione. Se si desidera vedere i risultati per te si dovrebbe essere in grado di solo copia/incolla direttamente:
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
Ho provato a fare funzionare l'applicazione in configurazione Release
oltre a gestire la configurazione di rilascio senza il debugger di Visual Studio allegato.Ecco i risultati emessi:
uscita con Visual Studio Debugger:
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
uscita senza Debugger di 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
Guardando questi risultati sarebbe guardare come se usando DirectCast()
non ha quasi nessun riscontro in termini di prestazioni rispetto al compilatore che aggiunge nel cast. Tuttavia, quando si fa affidamento sull'oggetto per essere rilegato in ritardo, si verifica un errore di prestazioni ENORME e l'esecuzione rallenta notevolmente. Quando si utilizza System.Reflection
, si verifica un leggero rallentamento rispetto alla trasmissione diretta dell'oggetto. Ho pensato che fosse insolito che ottenere il numero PropertyInfo
fosse molto più veloce rispetto all'utilizzo del metodo .InvokeMember()
.
Conclusione: Quando possibile uso DirectCast()
o lanciare direttamente l'oggetto. Usare il riflesso dovrebbe essere riservato solo quando ne hai bisogno. Utilizzare gli oggetti in ritardo solo come ultima risorsa. Sinceramente però, se stai solo modificando un oggetto alcune volte qua o là, è probabile che non abbia importanza nel grande schema delle cose.
Invece dovresti essere più preoccupato di come quei metodi potrebbero fallire e come impedirgli di farlo. Ad esempio, nel metodo SetObjectInvokeMember()
, se l'oggetto sender
non aveva la proprietà MyProperty
, quel bit di codice avrebbe generato un'eccezione. Nel metodo SetObjectReflection()
, le informazioni sulle proprietà restituite potevano essere nothing
che avrebbe comportato un NullReferenceException
.
Non correlato alla domanda ma suggerisco vivamente di abilitare Option Strict su tutti i progetti. –
Se si conosce C#, quindi, in base a [questa risposta] (http://stackoverflow.com/a/40782/240733), 'DirectCast (obj, T)' è uguale a '(T) obj' di C#. Se ciò è corretto, probabilmente è il tipo di cast più efficiente (con la magia meno nascosta) che puoi eseguire; seguito da 'TryCast' (' as' in C#). – stakx
@the_lotus Attivando 'Option Strict On' si disabilita la possibilità di utilizzare l'associazione tardiva nella maggior parte dei casi che renderebbe questa domanda discutibile. Tuttavia, poiché il solito valore predefinito per Visual Studio è 'Option Strict Off' (boo Microsoft), e il fatto che l'associazione tardiva sia utile a volte (guardando te, LINQ e' System.Reflection'), penserei che questa domanda merita una risposta –