I have a object type variable (control .Tag) that I need to cast to a structured type, and change a member in. This is a contrived but representative example:
Public Structure struct_COLOURS
Dim ILikeRed as boolean
Dim ILikeGreen as boolean
End Structure
Dim AnObject as Object = (some source that is struct_COLOURS)
DirectCast(AnObject, struct_COLOURS).ILikeRed = True ' This is not valid syntax?!
I don't remember my C syntax very well, but it would be something like this:
(struct_COLOURS*)AnObject->ILikeRed = true;
The point is I can cast an object to something and set members in the resulting cast. It seems as though DirectCast is actually a function and is not casting in the way I would interpret it.
Oddly, if you only want to retrieve a member value, you can use DirectCast:
dim YummyRed AS Boolean = DirectCast(AnObject, struct_COLOURS).ILikeRed
is just fine!
If I cannot cast the way I want, and I cannot change the use of the Tag property (so please don't suggest, it's not an option) to store these structures, what is the fastest way to set members?
It seems as though DirectCast is actually a function and is not casting in the way I would interpret it.
No, that’s wrong: DirectCast isn’t a method, it’s a real language construct, like a cast in C.
However, if you store a structure (= value type) in an object, it gets boxed and, by consequence, copied. This is causing the problem here: you’re attempting to modify a copy, not the original, boxed object.
So in order to change a member of a boxed value type object, you need to copy the object, change its value, and copy it back:
Dim tmp = DirectCast(AnObject, struct_COLOURS)
tmp.ILikeRed = True
AnObject = tmp
Incidentally, the same is true in C#, despite the superficial similarity to the C cast syntax.
That's how you should cast - with CType:
Dim myColor As Object = Nothing
Dim color As Color = CType(myColor, Color)
color.Name = "red"
Why a struct and not a class?
Related
In the below code "f" is an instance of the Class FORM which has a property "s" of type SIZE, a structure which has been defined in the code. My question is: When I try to assign values to the attributes of property "s" of the instance "t" directly it does not work: That is the statement f.s.height = 15 does not work. My confusion is arising from the fact that when I print the values of the property "s" of the instance "f", I am able to print the individual attributes of the structure SIZE but the same cannot be done while assignment of value. Assignment of values require me to call the constructor. Why is it so? What is preventing the assignment of the value to the attributes of "s": i.e. f.s.height & f.s.width?
Module Module1
Sub Main()
Dim f As New MyForm()
f.s = New Size(2, 5) 'Works Fine
f.colour = "Red" 'Assignment works just fine
'Below: Individual elements cannot be acceessed for assignment. WHY?
f.s.height = 15 'Doesn't Work
f.s.height = +2 'Doesn't work
'Individual elements can be accessed while printing
Console.WriteLine("Widht = {0}", f.s.width)
Console.WriteLine("Height = {0}", f.s.height)
Console.ReadLine()
End Sub
End Module
Class MyForm
Public Property s As Size
Public Property colour As String
End Class
Public Structure Size
Dim height As Integer
Dim width As Integer
Public Sub New(ByVal w As Integer, ByVal h As Integer)
width = w
height = h
End Sub
End Structure
Pls help.
The compiler should be indicating "Expression is a value and therefore cannot be the target of an assignment".
Change Size from a Structure to a Class (and Dim to Property) to fix the problem:
Public Class Size
Property height As Integer
Property width As Integer
Public Sub New(w As Integer, h As Integer)
width = w
height = h
End Sub
End Class
By the way, you'll also see this behaviour with the standard System.Drawing.Size which is defined as a Structure rather than a Class. (So is Point and probably others.)
This behavior is fundamental to value types (Structures). Conceptually, an instance of a value type is supposed to represent a single immutable value, and any instances with the same value are all supposed to be equivalent. As you have observed, you can get very surprising behavior if you try to change parts of an existing value type. It's really not intended for you to be able to alter them piecewise.
For this reason, I will always recommend that the members of a value type should be marked as ReadOnly so that you get blocked from trying to change them after construction.
If you want to be able to treat something like a mutable object instance instead of an immutable value, it needs to be a reference type (a Class). That's what they're designed to do.
After a lot of searching I came across the following article which probably explains the reason why we are not able to directly access the height/width attribute of the SIZE structure through an instance of the FORM class. Requesting people to go through this as the author has given a lot of details:
http://www.albahari.com/valuevsreftypes.aspx
PLs feel free to share any difference in opinion.
I can't get the code below working.
The error is in the last line: Type 'ChangeType' is not defined.
Does the compiler thinks that ChangeType is a customtype which i did't have defiened ?
I have no clue, plz give me a hint.
May be I can't see the forest for the trees.
Dim DataValue as String = "True"
Dim ChangeTypeIndex() As String = {"System.Boolean", "System.Char", "System.SByte", "System.Byte", "System.Int16"}
Dim ChangeType As Type = Type.GetType(ChangeTypeIndex(0))
Dim Result = DirectCast(DataValue, ChangeType)
This is not possible in VB.NET. VB.NET is a type-safe language, and the express purpose of DirectCast is to aid in the compile-time type checking. Since it is analyzed for correctness at compile-time, it, by definition, can't be given a variable for the type. DirectCast can only be used to cast an object to another directly-related type (by inheritance or implementation). Since DataValue is a String, you couldn't cast it to a Boolean anyway (since String doesn't inherit from Boolean), even if DirectCast did allow you to pass a variable type like that.
.NET does support reflection and late-binding, so it is possible to do the same kind of thing, if you really need to, but it's generally a good idea to avoid these kinds of things as much as you can, so as to ensure that you are getting the most benefit out of the compiler's type-checking safety measures.
Warnings aside, if you really need to do this, a close approximation would be something like this:
Option Strict Off
' ...
Dim dataValue As String = "True"
Dim changeTypeIndex() As String = {"System.Boolean", "System.Char", "System.SByte", "System.Byte", "System.Int16"}
Dim changeType As Type = Type.GetType(changeTypeIndex(0))
Dim o As Object = Activator.CreateInstance(changeType)
Dim result As Object = o.Parse(dataValue)
Console.WriteLine(result.GetType().Name) ' Outputs "Boolean"
Console.WriteLine(result) ' Outputs "True"
Not sure what you are trying to do, but here is some code to play with. Note that I have changed the 'types' in the array to contain valid type names.
Dim DataValue As String = "True"
Dim ChangeTypeIndex() As String = {"System.Boolean", "System.Char", "System.SByte", "System.Byte", "System.Int16"}
Dim ChangeType As Type
For x As Integer = 0 To ChangeTypeIndex.Length - 1
ChangeType = Type.GetType(ChangeTypeIndex(x), True)
Next
ChangeType = Type.GetType(ChangeTypeIndex(0), True)
Dim Result As Object = CTypeDynamic(DataValue, ChangeType)
Coming from Basic boolean logic in C#, I was wondering why:
Dim b As Boolean
Dim obj As Object = Nothing
'followig evaluates to False'
b = DirectCast(Nothing, Boolean)
'This throws an "Object reference not set to an instance of an object"-Exception'
b = DirectCast(obj, Boolean)
A CType(obj, Boolean) would evaluate to False(just as CBool(obj)). I think it is because the compiler uses a helper function, but that is not my theme.
Why does casting Nothing to Boolean evaluates to False, whereas casting an object that is Nothing to Boolean throws an Nullreference-Exception? Does that make sense?
[Option Strict ON]
Presumably, this is because Nothing in VB.NET is not exactly the same thing as null in C#.
In the case of value types, Nothing implies the default value of that type. In the case of a Boolean, the default value is False, so the cast succeeds.
One of the primary differences between value types such as Integer or structures and reference types such as Form or String is that reference types support a null value. That is to say, a reference type variable can contain the value Nothing, which means that the variable doesn't actually refer to a value. In contrast, a value type variable always contains a value. An Integer variable always contains a number, even if that number is zero. If you assign the value Nothing to a value type variable, the value type variable just gets assigned its default value (in the case of Integer, that default value is zero). There is no way in the current CLR to look at an Integer variable and determine whether it has never been assigned a value - the fact that it contains zero doesn't necessarily mean that it hasn't been assigned a value.
–The Truth about Nullable Types and VB...
EDIT: For further clarification, the reason the second example throws a NullReferenceException at run-time is because the CLR is attempting to unbox the Object (a reference type) to a Boolean. This fails, of course, because the object was initialized with a null reference (setting it equal to Nothing):
Dim obj As Object = Nothing
Remember that, as I explained above, the VB.NET keyword Nothing still works the same way as null in C# when it comes to reference types. That explains why you're getting a NullReferenceException because the object you're attempting to cast is literally a null reference. It does not contain a value at all, and therefore cannot be unboxed to a Boolean type.
You don't see the same behavior when you try to cast the keyword Nothing to a Boolean, i.e.:
Dim b As Boolean = DirectCast(Nothing, Boolean)
because the keyword Nothing (this time, in the case of value types) simply means "the default value of this type". In the case of a Boolean, that's False, so the cast is logical and straightforward.
There's a couple of things you have to realize here.
The first is what others have already pointed out: Nothing can be interpreted by the VB compiler as simply the Boolean value False given the proper context, such as Dim b As Boolean = Nothing.
This means that when the compiler sees this:
b = DirectCast(Nothing, Boolean)
It sees a literal (Nothing) and also sees that you want to use this literal as a Boolean. That makes it a no-brainer.
But now here's the second thing you have to realize. DirectCast on an Object is essentially an unboxing operation (for value types). So what needs to happen from the VB compiler's perspective is: there needs to be a Boolean in that box, or else the operation will fail. Since there is in fact nothing in the box—and this time we're really talking nothing, as in null—it throws an exception.
If I were to translate this code to C#, it would look like this:
bool b;
object obj = null;
b = (bool)default(bool);
b = (bool)obj;
Hopefully that makes things a bit clearer?
There's a difference between using the keyword (literal) Nothing, and using a reference variable whose value is Nothing.
In VB.NET, the literal (keyword) Nothing gets special treatment. The Nothing keyword can be automatically converted into a value type, using the default value of that type.
A reference variable whose value is Nothing is different. You don't get the special behaviour.
The documentation says DirectCast "requires an inheritance or implementation relationship between the data types of the two arguments".
Clearly Object does not inherit from or implement Boolean, unless you have put a boxed Boolean into an object variable.
So the code below fails at runtime with an exception.
Dim obj As Object = Nothing
b = DirectCast(obj, Boolean)
To get the expected behavior, you need this code:
Dim b As Boolean?
Dim obj As Object = Nothing
b = DirectCast(obj, Boolean?)
The character ? mean Nullable(of ).
I'm finding that comparing of the Boolean variable to a string of "True", "False" or Is nothing seems to ensure that I get the correct comparisons. I was using a function to return an html string of a div with an image of a checked or unchecked radio button and was having the issue of nothing coming back as false. Using the variable = "True" or "False" string and doing the last check with IS NOTHING helped to resolve that issue.
Dim b as boolean = nothing
response.write CheckValue(b = "True")
response.write (b = "False")
response.write (b is nothing)
Function CheckValue(inVal as boolean) as string
if inVal then
return ("<div><img src="checked.png" ></div>
else
return ("<div><img src="unchecked.png" ></div>
end if
end function
The system seems to do the conversion to string when implicitly compared to a string whereas using the .tostring method just creates an error while allowing the last comparison to actually compare to a value of nothing.
Hopefully that helps somewhat. It at least let me
I would like to to copy that values of an array into a Structure.
Example:
' The Array
Dim Columns(2) As String
' The Structure
Private Structure Fields
Public FName As String
Public LName As String
Public Email As String
End Structure
' I would like to map it like so:
Fields.FName = Columns(0)
Fields.LName = Columns(1)
Fields.Email = Columns(2)
Obviously I could write a function if it was so simple, but really there are over 25 columns and it's a pain to write a function that would map it.
Is there some way to do this?
There really is no simple way that will work in all cases. What you are complaining is too much effort is the only way to guarantee that it will work in all cases.
That said, if you can guarantee that the number of elements in the array matches the number of properties/fields in the structure/class and that they are in the same order and of the same types then you could use Reflection in a loop, e.g.
Private Function Map(source As Object()) As SomeType
Dim result As New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return result
End Function
EDIT:
The code I have provided works as is if SomeType is a class but, as I missed the first time around, not for a structure. The reason is that structures are value types and therefore a copy of the original object is being sent to SetValue, so the field value never gets set on that original object. In theory, to prevent a copy being created, you should be able to simply box the value, i.e. wrap it in an Object reference:
Private Function Map(source As Object()) As SomeType
Dim result As Object = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function
As it turns out though, the VB compiler treats that a little differently than the C# compiler treats the equivalent C# code and it still doesn't work. That's because, in VB, the boxed value gets unboxed before being passed to the method, so a copy is still created. In order to make it work in VB, you need to use a ValueType reference instead of Object:
Private Function Map(source As Object()) As SomeType
Dim result As ValueType = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function
I have no idea on how to cast an object that type was 'Object' to a user defined class type.
I have a private instance variable:
Private studyType as Object
What i need to do is to instantiate this object from an event handling method. And no, not to instance new Object().
Basically it would look like this:
studyType = new VCEOnly()
However, I am only allowed to use the Object class subs and functions as the type was defined as Object. So i need to cast it to VCEOnly class type so i can access its subs and functions.
Basically, studyType needs to be casted from Object to VCEOnly. I am not allowed to pre-define studyType as VCEOnly when declared.
you can also use:
dim studyType as Object = new VCEOnly()
...
dim studyTypeVCE as VCEOnly = nothing
if trycast(studytype,VCEOnly) IsNot Nothing then
studyTypeVCE = DirectCast(studytype,VCEOnly)
'... do your thing
end if
the if statement checks if the object can be casted to the wanted type and if so variable of type VCEOnly will be filled in with a cast of studytype.
Use CType to cast an object from one type to another
Something like this should do it:
Dim studyType as Object
Dim studyTypeVCE as New VCEOnly
studyTypeVCE = Ctype(studyType,VCEOnly)
or you can just do this:
With CType(studyType, VCEOnly)
.SomeVCEOnlyProperty = "SomeValue"
End With