简体   繁体   English

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

[英]Why Cast (CType/DirectCast) controls versus implicit conversion

Suppose I have a control within a webforms GridViewRow... 假设我在webforms GridViewRow中有一个控件...

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

Within the RowDataBound event I can access the control using any of the following methods. 在RowDataBound事件中,我可以使用以下任何方法访问控件。 I've always used DirectCast historically: 我一直以来一直使用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")

Is there any advantage of using any particular approach? 使用任何特定方法有什么好处吗? I guess DirectCast is slightly more efficient, but possibly prone to errors, but are there any dangers of the implicit cast (option 3)? 我猜DirectCast稍微有点效率,但可能容易出错,但隐式转换是否有任何危险(选项3)?

Historically I've never seen any errors until I try to actually assign a value to the control's property, which makes me think that this first step isn't really that important? 从历史上看,在我尝试为控件的属性实际赋值之前,我从未见过任何错误,这让我觉得这第一步不是那么重要吗?

Please note this is not meant to be a DirectCast vs CType discussion, more about whether casting is even necessary here? 请注意,这不是DirectCast与CType的讨论,更多的是关于在这里是否需要施法?

Update for clarity 更新清晰

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)

Why therefore should a developer cast (in this example), or is this example just too simple? 那么为什么开发人员应该投射(在这个例子中),或者这个例子过于简单?

For your situation, TryCast with an IsNot Nothing check might be more beneficial. 对于您的情况,使用IsNot Nothing检查的TryCast可能更有益。

To understand when and why to use which, though, first take a look at the MSDN definitions of them. 但是,要了解何时以及为何使用哪个,首先要看一下它们的MSDN定义。

DirectCast DirectCast

Introduces a type conversion operation based on inheritance or implementation. 介绍基于继承或实现的类型转换操作。 ... DirectCast does not use the Visual Basic run-time helper routines for conversion ... ... DirectCast 不使用Visual Basic运行时帮助程序进行转换 ...

CType CTYPE

Returns the result of explicitly converting an expression to a specified data type , object, structure, class, or interface. 返回将表达式显式转换为指定数据类型 ,对象,结构,类或接口的结果。

Implicit Conversion 隐式转换

An implicit conversion does not require any special syntax in the source code. 隐式转换不需要源代码中的任何特殊语法。 ... An explicit conversion uses a type conversion keyword ...显式转换使用类型转换关键字

TryCast TryCast

Introduces a type conversion operation that does not throw an exception. 介绍一种不会引发异常的类型转换操作。 ... TryCast returns Nothing (Visual Basic), so that instead of having to handle a possible exception, you need only test the returned result against Nothing. ... TryCast返回Nothing(Visual Basic),因此您只需要针对Nothing测试返回的结果,而不必处理可能的异常。

Going off of those definitions, we can assume that CType will make an external call based on a given System.Type , while DirectCast will just use the existing object under a different moniker. 除了这些定义之外,我们可以假设CType将基于给定的System.Type进行外部调用,而DirectCast将仅使用不同名字对象下的现有对象。 Meanwhile, with implicit conversion, VB will just try to execute the code. 同时,通过隐式转换,VB将尝试执行代码。 TryCast , however, will try to cast the object or just return Nothing (think of the C# as operator) 但是, TryCast将尝试转换对象或只返回Nothing (将C# as运算符)

For example: 例如:

' 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)

The first example works, because obj is already a String that has just been defined as an Object . 第一个示例,因为obj已经是一个String刚刚被定义为一个Object No actual conversion is taking place. 没有实际的转换。

Now let's look at CType : 现在让我们来看看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)

And finally, implicit conversion: 最后,隐式转换:

' 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

Both of these work, but remember that VB.NET is calling a separate library here to do the dirty work: 这两个都有用,但请记住,VB.NET在这里调用一个单独的库来完成这项肮脏的工作:

DirectCast: 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/Implicit Conversion (compiles the same): 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

So, basically, due to .NET needing to call external methods to determine what it needs to do to convert the object, CType /implicit will run somewhat slower ( example benchmarks and examples here ). 所以,基本上,由于.NET需要调用外部方法来确定转换对象需要做什么, CType / implicit将运行得稍慢( 这里的示例基准和示例 )。 Note, since they both compile the same in MSIL, CType and implicit conversion should perform identically. 注意,因为它们都在MSIL中编译相同,所以CType和隐式转换应该执行相同的操作。

So when do you use them? 那么你什么时候使用它们? I generally follow a couple simple rules 我通常遵循一些简单的规则

  1. If I know (or expect) my object is already my target type, just defined differently, I use DirectCast 如果我知道(或期望)我的对象已经是我的目标类型,只是定义不同,我使用DirectCast
  2. If my object is different type than my target type, I use the appropriate Convert method. 如果我的对象类型不同于我的目标类型,我使用适当的Convert方法。 Example: Dim myInt = CInt("42") . 示例: Dim myInt = CInt("42") Note, this compiles the same way as CType in the IL 注意,这与IL中的CType编译方式相同
  3. If I am unsure of the incoming type, I use TryCast 如果我不确定传入的类型,我使用TryCast
  4. If I'm casting/converting with generics, I'll use DirectCast and/or Convert.ChangeType , depending on the context 如果我使用泛型进行转换/转换,我将使用DirectCast和/或Convert.ChangeType ,具体取决于上下文

You could also use CType for the second one there, but in my opinion, if I know I'm converting to an Integer , then I'll choose the more explicit CInt . 您也可以使用CType作为第二个,但在我看来,如果我知道我正在转换为Integer ,那么我将选择更明确的CInt If you have Option Strict on, though, you should get a build error either way if you pass the wrong thing into either. 但是,如果你有Option Strict ,那么如果你把错误的东西传递给任何一个,你应该得到一个构建错误。

Also, while you might tempted to try substituting TryCast for DirectCast check the answer to this SO question regarding major differences and uses: Why use TryCast instead of Directcast? 此外,虽然您可能想尝试将TryCast替换为DirectCast请检查有关主要差异和用途的此SO问题的答案: 为什么要使用TryCast而不是Directcast?

If you notice, I did not include implicit typing in there. 如果你注意到,那里我没有包含隐式输入。 Why? 为什么? Well, mostly because I code with Option Strict On , and it doesn't really allow implicit conversions when narrowing types (see "Widening and Narrowing Conversions" ). 好吧,主要是因为我使用Option Strict On编码,并且在缩小类型时它并不真正允许隐式转换(请参阅“扩展和缩小转换” )。 Otherwise, as far as the .NET is concerned, it's pretty much identical to CType 否则,就.NET而言,它与CType几乎完全相同

Ok, now that all of that's done, let's look at all three (four, I guess) with Control objects: 好了,现在已经完成所有这些,让我们看一下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)

And one more: 还有一个:

' 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)

So, for your situation, it looks like TryCast with a IsNot Nothing check is the way to go. 因此,对于您的情况,看起来TryCast带有IsNot Nothing检查是要走的路。

You might want to look at the answer here: 你可能想看看这里的答案:

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

As part of your question boils down to DirectCast versus CType. 您的问题的一部分归结为DirectCast与CType。

As to why you shouldn't use the implicit casting, you have already called out some of the drawbacks in your question (ie works if not found etc.). 至于为什么你不应该使用隐式强制转换,你已经在你的问题中找到了一些缺点(即如果找不到则工作等)。

To answer your question on whether casting is necessary, "It Depends". 要回答关于是否有必要进行铸造的问题,“它取决于”。

In the example you give, it is necessary because you are declaring your variables as type Literal . 在您给出的示例中,这是必要的,因为您将变量声明为Literal类型。

The FindControl method returns a Control , which Literal inherits from. FindControl方法返回Literal继承的Control So you could could conceivably declare ltl_abc as a Control (and thereby avoid the need to cast it to Literal ), assuming that you don't need to access any of the properties or methods that are specific to Literal . 因此,您可以想象将ltl_abc声明为Control (从而避免将其ltl_abcLiteral ),假设您不需要访问特定于Literal任何属性或方法。

DirectCast() is the most efficient and speedy, but will throw an exception when the target is not exactly the type you are looking for, or null. DirectCast()是最有效和最快速的,但是当目标不是您正在寻找的类型时会抛出异常,或者为null。

TryCast() (which you didn't list) is pretty much the same thing as DirectCast() , but never throws an exception, instead returning null. TryCast() (你没有列出)与DirectCast()完全相同,但从不抛出异常,而是返回null。

CType() is less efficient, and will only throw an exception if no conversion is available between the target object and the destination object type. CType()的效率较低,如果目标对象和目标对象类型之间没有可用的转换,则只会抛出异常。 If a conversion exists, then it will run that conversion, and that conversion MAY fail if the target is null. 如果存在转换,则它将运行该转换,如果目标为null,则转换可能会失败。

Explicit will not perform a conversion for you automatically, and will not throw an exception if the type is not an exact match (IE, assign an array to an IEnumerable variable), and will not throw an exception if the target is null. 显式不会自动执行转换,并且如果类型不是完全匹配(IE,将数组分配给IEnumerable变量),则不会抛出异常,并且如果目标为null,则不会抛出异常。 So you may end up with an exception when trying to access that object later. 因此,稍后尝试访问该对象时可能会出现异常。 So it's pretty close to TryCast() without having a problem with assigning objects to compatible objects. 所以它非常接近TryCast()而没有将对象分配给兼容对象的问题。

For your example there, I'd recommend DirectCast() with some exception handling. 对于你的例子,我建议DirectCast()进行一些异常处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM