为什么Cast(CType / DirectCast)控件与隐式转换

时间:2013-12-12 10:21:25

标签: asp.net vb.net asp.net-4.5

假设我在webforms GridViewRow中有一个控件...

<asp:Literal ID="ltl_abc" runat="server" />

在RowDataBound事件中,我可以使用以下任何方法访问控件。我一直以来一直使用DirectCast:

Protected Sub gv_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles gv.RowDataBound
    Select Case e.Row.RowType
        Case DataControlRowType.DataRow
            ' 1) Dim ltl_abc As Literal = DirectCast(e.Row.FindControl("ltl_abc"), Literal)
            ' 2) Dim ltl_abc As Literal = CType(e.Row.FindControl("ltl_abc"), Literal)
            ' 3) Dim ltl_abc As Literal = e.Row.FindControl("ltl_abc")

使用任何特定方法有什么好处吗?我猜DirectCast稍微有点效率,但可能容易出错,但隐式转换是否有任何危险(选项3)?

从历史上看,在我尝试为控件的属性实际赋值之前,我从未见过任何错误,这让我觉得第一步不是那么重要吗?

请注意,这不是DirectCast与CType的讨论,更多关于是否需要在此进行投射?

为清晰起见而更新

Protected Sub gv_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles gv.RowDataBound
    Select Case e.Row.RowType
        Case DataControlRowType.DataRow

            ' This works fine, but no explicit casting is done:
            Dim ltl_abc As Literal = e.Row.FindControl("ltl_abc") ' no (explicit) cast
            ltl_abc.Text = "Hello World"

            ' This also works until I try to access the object's properties
            Dim ltl_abc As Literal = DirectCast(e.Row.FindControl("NonExistentId"), Literal)

为什么开发人员应该投射(在这个例子中),或者这个例子过于简单?

4 个答案:

答案 0 :(得分:8)

对于您的情况,TryCast进行IsNot Nothing检查可能会更有益。

要了解何时以及为何使用哪个,请先查看它们的MSDN定义。

<强> DirectCast

  

介绍基于继承或实现的类型转换操作。 ... DirectCast 不使用Visual Basic运行时帮助程序进行转换 ...

<强> CType

  

返回将表达式显式转换为指定数据类型,对象,结构,类或接口的结果。

<强> Implicit Conversion

  

隐式转换不需要源代码中的任何特殊语法。 ...显式转换使用类型转换关键字

<强> TryCast

  

引入不会引发异常的类型转换操作。 ... TryCast返回Nothing(Visual Basic),因此您只需要针对Nothing测试返回的结果,而不必处理可能的异常。

关于这些定义,我们可以假设CType将基于给定的System.Type进行外部调用,而DirectCast将仅使用不同名字对象下的现有对象。同时,通过隐式转换,VB将尝试执行代码。但是,TryCast会尝试转换对象或只返回Nothing(想想C#as运算符)

例如:

' works
Dim obj As Object = "I'm a string!" 'obj.GetType() -> System.String
Dim s = DirectCast(obj, String)

' throws error: Unable to cast object of type 'System.Int32' to type 'System.String'.
Dim obj As Object = 42 'obj.GetType() -> System.Int32
Dim s = DirectCast(obj, String)

第一个示例有效,因为obj已经是String,刚被定义为Object。没有实际转换。

现在让我们看一下CType

' works
Dim obj As Object = "I'm a string!" 'obj.GetType() -> System.String
Dim s = CType(obj, String)

' works - would prefer to use CStr() here instead, since it's more explicit (see below)
Dim obj As Object = 42 'obj.GetType() -> System.Int32
Dim s = CType(obj, String)

最后,隐式转换:

' works with Option Explicit. Throws build error with Option Strict: Option Strict On disallows implicit conversions from 'Object' to 'String'.
Dim obj As Object = "I'm a string!"    'obj.GetType() -> System.String
Dim s As String = obj

' same as above
Dim obj As Object = 42 'obj.GetType() -> System.Int32
Dim s As String = obj

这两个都有用,但请记住VB.NET在这里调用一个单独的库来完成这项肮脏的工作:

<强> DirectCast:

IL_0000: nop
IL_0001: ldstr "I'm a string!"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: castclass [mscorlib]System.String
IL_000d: stloc.1
IL_000e: nop
IL_000f: ret

CType /隐式转换(编译相同):

IL_0000: nop
IL_0001: ldstr "I'm a string!"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: call string [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToString(object)
IL_000d: stloc.1
IL_000e: nop
IL_000f: ret

因此,基本上,由于.NET需要调用外部方法来确定转换对象需要做什么,CType / implicit将运行得稍慢(example benchmarks and examples here)。注意,因为它们都在MSIL中编译相同,CType和隐式转换应该执行相同的操作。

所以你什么时候使用它们?我通常遵循一些简单的规则

  1. 如果我知道(或期望)我的对象已经是我的目标类型,只是定义不同,我使用DirectCast
  2. 如果我的对象类型与目标类型不同,我使用相应的Convert方法。示例:Dim myInt = CInt("42")。请注意,这与IL
  3. 中的CType编译方式相同
  4. 如果我不确定传入类型,我会使用TryCast
  5. 如果我使用泛型进行转换/转换,我将使用DirectCast和/或Convert.ChangeType,具体取决于上下文
  6. 你也可以使用CType作为第二个,但在我看来,如果我知道我正在转换为Integer,那么我会选择更明确的CInt }。但是,如果你有Option Strict,那么如果你把错误的东西传递给任何一个,你都应该得到一个构建错误。

    此外,虽然您可能想尝试用TryCast替换DirectCast来检查有关主要差异和用途的此SO问题的答案:Why use TryCast instead of Directcast?

    如果你注意到,那里我没有包含隐式输入。为什么?好吧,主要是因为我使用Option Strict On进行编码,并且在缩小类型时它并不真正允许隐式转换(请参阅"Widening and Narrowing Conversions")。否则,就.NET而言,它与CType

    几乎完全相同

    好的,现在已经完成了所有这些,让我们看一下Control个对象的所有三个(我猜):

    ' control is just defined as a regular control
    Dim control As New Control    
    
    ' Runtime Error: Unable to cast object of type 'System.Web.UI.Control' to type 'System.Web.UI.LiteralControl'
    Dim literal_1 As LiteralControl = DirectCast(control, LiteralControl)
    
    ' Runtime Error: Unable to cast object of type 'System.Web.UI.Control' to type 'System.Web.UI.LiteralControl'
    Dim literal_2 As LiteralControl = CType(control, LiteralControl)
    
    ' returns literal_3 -> Nothing
    Dim literal_3 As LiteralControl = TryCast(control, LiteralControl)
    

    还有一个:

    ' control as a LiteralControl stored as a Control
    Dim control As Control = New LiteralControl 
    
    ' works
    Dim literal_1 As LiteralControl = DirectCast(control, LiteralControl)
    
    ' works
    Dim literal_2 As LiteralControl = CType(control, LiteralControl)
    
    ' works
    Dim literal_3 As LiteralControl = TryCast(control, LiteralControl)
    

    因此,对于您的情况,看起来TryCast的{​​{1}}检查是可行的。

答案 1 :(得分:1)

您可能希望在此处查看答案:

https://stackoverflow.com/a/3056582/117215

作为问题的一部分归结为DirectCast与CType。

至于为什么你不应该使用隐式强制转换,你已经在你的问题中找到了一些缺点(即如果找不到则可以工作等)。

答案 2 :(得分:1)

要回答关于是否需要施法的问题,“它取决于”。

在您给出的示例中,有必要因为您将变量声明为Literal类型。

FindControl方法返回ControlLiteral继承自ltl_abc。因此,假设您不需要访问任何属性或方法,您可以将Control声明为Literal(从而避免将其强制转换为Literal)特定于{{1}}。

答案 3 :(得分:0)

DirectCast()是最有效和最快速的,但是当目标不是您正在寻找的类型时会抛出异常,或者为null。

TryCast()(你没有列出)与DirectCast()几乎相同,但从不抛出异常,而是返回null。

CType()效率较低,如果目标对象和目标对象类型之间没有可用的转换,则只会抛出异常。如果存在转换,那么它将运行该转换,如果目标为空,则转换可能会失败。

Explicit不会自动执行转换,如果类型不是完全匹配(IE,将数组分配给IEnumerable变量),则不会抛出异常,如果目标为null,则不会抛出异常。因此,稍后尝试访问该对象时可能会出现异常。所以它非常接近TryCast()而没有将对象分配给兼容对象的问题。

对于那里的例子,我建议DirectCast()进行一些异常处理。