Pergunta

Suppose I have a control within a webforms GridViewRow...

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

Within the RowDataBound event I can access the control using any of the following methods. I've always used DirectCast historically:

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

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?

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?

Foi útil?

Solução

For your situation, TryCast with an IsNot Nothing check might be more beneficial.

To understand when and why to use which, though, first take a look at the MSDN definitions of them.

DirectCast

Introduces a type conversion operation based on inheritance or implementation. ... DirectCast does not use the Visual Basic run-time helper routines for conversion...

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

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.

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. Meanwhile, with implicit conversion, VB will just try to execute the code. TryCast, however, will try to cast the object or just return Nothing (think of the C# as operator)

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. No actual conversion is taking place.

Now let's look at 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:

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

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). Note, since they both compile the same in MSIL, CType and implicit conversion should perform identically.

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
  2. If my object is different type than my target type, I use the appropriate Convert method. Example: Dim myInt = CInt("42"). Note, this compiles the same way as CType in the IL
  3. If I am unsure of the incoming type, I use TryCast
  4. If I'm casting/converting with generics, I'll use DirectCast and/or Convert.ChangeType, depending on the context

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. If you have Option Strict on, though, you should get a build error either way if you pass the wrong thing into either.

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?

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"). Otherwise, as far as the .NET is concerned, it's pretty much identical to CType

Ok, now that all of that's done, let's look at all three (four, I guess) with Control objects:

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

Outras dicas

You might want to look at the answer here:

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

As part of your question boils down to DirectCast versus CType.

As to why you shouldn't use the implicit casting, you have already called out some of the drawbacks in your question (i.e. 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.

The FindControl method returns a Control, which Literal inherits from. 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.

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.

TryCast() (which you didn't list) is pretty much the same thing as DirectCast(), but never throws an exception, instead returning 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. If a conversion exists, then it will run that conversion, and that conversion MAY fail if the target is 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. 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.

For your example there, I'd recommend DirectCast() with some exception handling.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top