I was shocked just a moment ago to discover that the following is legal (the C# equivalent is definitely not):
Class Assigner
''// Ignore this for now.
Public Field As Integer
''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
''// But... what's this?!?
Sub AssignNew()
''// Passing "Me" ByRef???
Assign(Me, New Assigner)
End Sub
''// This is just for testing.
Function GetField() As Integer
Return Me.Field
End Function
End Class
But what's even stranger just as strange to me is that it doesn't seem to do what I expect:
Dim a As New Assigner With {.Field = 10}
a.AssignNew()
Console.WriteLine(a.GetField())
The above outputs "10," not "0" like I thought it would (though naturally, this expectation was itself infused with a certain kind of horror). So it seems that you can pass Me ByRef, but the behavior is somehow overridden (?) by the compiler to be as if you had passed Me ByVal.
Why is it legal to pass Me ByRef? (Is there some backwards-compatibility explanation?)
Am I correct in saying that the behavior of doing this is overridden by the compiler? If not, what am I missing?
This behavior actually follows pretty directly from the Visual Basic specification.
11.4.3 Instance Expressions
An instance expression is the keyword Me, MyClass, or MyBase. An instance expression, which may only be used within the body of a non-shared method, constructor, or property accessor, is classified as a value.
9.2.5.2 Reference Parameters
If the type of the variable being passed to a reference parameter is not compatible with the reference parameter's type, or if a non-variable is passed as an argument to a reference parameter, a temporary variable may be allocated and passed to the reference parameter. The value being passed in will be copied into this temporary variable before the method is invoked and will be copied back to the original variable (if there is one) when the method returns.
(All emphasis mine)
So, the compiler will create a temporary variable assigned to the value of Me to be passed as the ByRef parameter. Upon return, no copy of the resulting value will take place since Me is not a variable.
It appears the compiler transforms "Me" into a variable which is then passed ByRef. If you compile your code, then open it with Reflector, you can see what's happening:
Class Assigner
''// Methods
Public Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
Public Sub AssignNew()
Dim VB$t_ref$S0 As Assigner = Me
Me.Assign((VB$t_ref$S0), New Assigner)
End Sub
Public Function GetField() As Integer
Return Me.Field
End Function
''// Fields
Public Field As Integer
End Class
So it looks like when you call AssignNew(), you are assigning the new instance to the internally generated variable. The "a" variable doesn't get touched because it's not even a part of the function.
This is just one of the thousands of possible 'almost errors' a programmer can make. MS caught most of them, in fact, sometimes I'm suprised at how many warnings do come up.
they missed this one.
As far as why it doesn't change 'me', it's a darn good thing! When you use 'me', it just passes a copy of the real class you are working with, for safety purposes. If this worked they way you were hoping, we would be talking GIANT side-effect. You're innocently working away with in your class' methods, and them BAM all of a sudden you are in an ENTIRELY different object! That would be awful! If you're going to do that, you might as well just write a piece of spagetti MS-Basic line-numbered code with all globals that get randomly set, and no subs/functions.
The way this works is the same way if you pass arguments in parenthesis. For example this works as expected:
Assign(Reference_I_Want_To_Set, New Assigner)
But this doesn't change anything:
Assign((Reference_I_Want_To_Set), New Assigner)
If you reflect the above type of code as adam101 suggests you will see similar results. While that is huge frustration with the parenthesis, it is a very good thing with Me !!!
what you need to do to make this code work is this:
Class Assigner
''// Ignore this for now.
Private newPropertyValue As Integer
Public Property NewProperty() As Integer
Get
Return newPropertyValue
End Get
Set(ByVal value As Integer)
newPropertyValue = value
End Set
End Property
''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Shared Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
''// But... what's this?!?
Shared Sub AssignNew(ByRef x As Assigner)
''// Passing "Me" ByRef???
Assign(x, New Assigner)
End Sub
End Class
then use it like
Dim a As New Assigner With {.NewProperty = 10}
Assigner.AssignNew(a)
my understanding is you cannot change the reference of the object while using it, so you need to change it in a shared sub
since Me cannot be the target of an assignment, the code seem to create a copy of it and from that point on, your not using the real object, but a copy of it
Related
Solution: Just put brackets around Value in the CallByName statement to force evaluation of it.
Ex. CallByName MobClass, TargetData, vbLet, (Value)
Credit goes to Rory from the other post, which I will probably be deleting since it is no longer relevant and a possible duplicate.
I've spent a long time trying to figure out what was wrong with how I was using CallByName. I finally realized that its fourth argument (Args) will throw a type mismatch if the input is not either EXACTLY the same type as the input argument of what its calling or its hard-coded in.
(I don't even understand how, or why, it does this since VarType(Variant/Integer) = VarType(Integer))
So I either need a way to make it accept variant inputs or convert variables from Variant/Integer to Integer (or create a new variable) without a giant select case.
Edit: So my question wasn't clear so I'll explain it in more detail. I have a bunch of classes that I want to cycle through and call the Let property on. My simplified setup is:
Dim AllClasses as Collection
Sub SetAll(TargetProperty as String, Value as Variant)
For each ClassX in AllClasses
CallByName ClassX, TargetProperty, vbLet, Value
Next ClassX
End Sub
The problem is Value when it is initialized as Variant. The only time I can get it to not throw a type mismatch exception is when I initialize Value as the exact same type that the property wants, but I can't do that since the data types in the class vary.
Edit 2: I'm going to ask another question about the whole problem since no one seems to know much about CallByName
Edit 3: Here's a summary of what we have so far:
CallByName's fourth argument (Args) throws a type mismatch when trying to call the Let property on a class.
This only happens when the value trying to be assigned is stored in a Variant data type. It works perfectly if the variable is initialized to the same type the Let property is expecting OR if the value is hard-coded into the argument.
The Let property works fine on its own. It accepts Variant data types just fine.
My question is: Is there a way to stop this exception? I'm creating another post about other possible solutions to my overall problem.
The Problem is that you pass the properties-arguments by reference not by value, but you can't pass a reference to a different datatype (variant -> long) as the types don't match and it can't be converted as this would change the data type in the caller too. By using brackets, you force the argument to be passed by value and it can be casted as typeLong.
You can avoid this by using ByValin theProperty Letterinstead ofByRef(what is implicit used if not set). You are aware that by referencing a variable, changes made in the property change the callers value too?
Example:
Class PassExample
'Save as class module PassExample
Public Property Let PropByVal(ByVal NewVal As Long)
NewVal = 1
End Property
Public Property Let PropByRef(ByRef NewVal As Long)
NewVal = 1
End Property
Module with test sub:
'save as standard module
Sub test()
Dim v As Variant
v = 0
Dim ExampleInstance As New PassExample
CallByName ExampleInstance, "PropByVal", VbLet, v 'this works
CallByName ExampleInstance, "PropByRef", VbLet, v 'type mismatch
CallByName ExampleInstance, "PropByRef", VbLet, (v) 'this works as ByRef is changed to byval
Debug.Print v ' shows 0, not 1 as it should be if referenced
CallByName ExampleInstance, "PropByRef", VbLet, CVar(v) ' works too as it passes a function-result that can't be referenced
End Sub
Thanks to Rory and chris neilsen for providing the solution!
I'm trying to create a a simple function in VB.net which will take a few parameters for use with TryCast.
My goal is to have a function I can use in place of TryCast which instead of returning Nothing when it fails it should return DbNull.
I just can't seem to get anything to work despite much searching.
Thanks.
The problem is that you have to have some specific return type for your function. So you can write a function with a signature like this:
Public Function TryCast(Of T)(ByVal item As Object) As T
But you will never be able to make it do what you want, because the type of T is never going to be DBNull (okay, maybe it could happen, but hopefully you get the idea).
Put another way, the type system blocks this. On the one hand, you want to be perfectly explicit about the return type (hence, the cast). On the other hand, you want to be able to return a completely different type. .Net does not allow this, even with dynamic typing. Either a method returns a single fixed type, or it is not declared to return a type at all.
With Option Explicit turned off, you might be able to go for something like this:
Public Function TryCast(Of T)(ByVal item As Object)
However, I don't think this will do what you want, either, because now the returned value is effectively the same as object, and you lose the benefit of any casting.
It works with the normal TryCast() for Nothing, because Nothing can be assigned to any type. DBNull is itself a fixed type, and so is not as flexible.
Simple answer - not possible, and here is why. Suppose you have the following code:
Class A
End Class
Class B : Inherits A
End Class
Class C
End Class
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim aaa As A = MyTryCast(Of A)(New B)
Dim ccc As C = MyTryCast(Of B)(New C)
End Sub
Private Function MyTryCast(Of T As Class)(obj As Object) As Object
Dim result As Object = TryCast(obj, T)
If result Is Nothing Then Return Convert.DBNull
Return result
End Function
MyTryCast does exactly what you are asking for, so aaa assignment works. Problem appears when you assign DBNull to ccc variable of type C. You would expect it to assign DBNull to C, because C cannot be converted to B (regular TryCast would return Nothing). Instead, you get System.InvalidCastException, because you cannot assign a DBNull to an instance of your custom class. Important thing to remember here - Nothing can be assigned to anything, DBNull can only be assigned to an Object. See here for DBNull inheritance hierarchy.
In C++, it is possible to do:
int x;
int& foo = x;
// foo is now a reference to x so this sets x to 56
foo = 56;
But is an equivalent possible in Visual Basic .net (VB.net)?
I am aware of the ByRef term in VB.net but I have only seen (and by extensive research) this in passing parameters by reference to functions/subroutines, so that the function modifies the same variable declared by the calling code.
But I haven't seen ByRef used to declare variables (as class members or local variables). I have tried to use ByRef to do such a declaration but I get compiler errors for each ordering of the declaration I try.
The reason why I want to do this is because I want a class instance to be able to refer to a variable declared in another class instance.
Specifically, I am working on some existing VB.net code where program logic is mixed up with UI logic in VB.net form classes. I want to separate the program logic out of the form code so that it can be more easily reused. To do this I need the program logic code to be able to 'talk' to the form code so I need access to its variables.
I am using Visual Studio 2010 Express with VB.net and would prefer to remain within this environment even though I am aware that full Visual Studio has extended capabilities.
Pointers exist but they're called object references
(Now other posters, please don't quibble with me about the actual differences here. I am talking about the high level task the OP wants to accomplish)
Obviously you can't do literally what you've said - that is, surgically manipulate one member. But if you have control of the code, you can do almost as well. And from what you describe is your problem, this method will be much better, as you can pass references to an object that has many members you wish to update, instead of having to pass many individual pointers.
First define a class:
Class MyScreenValues
' Properties will work... using public fields for brevity
Public TextBox1Value as String
Public SpinControl1Value as Integer
public CheckBox1Value as Boolean
End Class
Now the equivalent of the code you posted:
Dim x as new MyScreenValues 'int x;
dim foo as MyScreenValues = x 'int& foo = x;
'// c++: foo is now a reference to x so this sets x to 56
'// .net: foo is now a reference to x so this sets x.SpinControl1Value to 56
foo.SpinControl1Value = 56;
If what you're doing is trying to pass pointers to, say, every control on your form's value to a sub, like so:
Button1_Click(...
Dim MyObject as new BusinessObject
MyObject.DoSubmit(TextBox1.Text, SpinButton1.Value, CheckBox1.Checked)
You can use the method provided by superbDeveloper, and use the ByRef keyword on the definition of the DoSubmit Sub:
Public Sub DoSubmit(ByRef Name as String, ByRef Age as Integer, ByRef Employed as boolean)
... business logic...
However this just gives you a 2-layer separation. Look into MVP, MVC, etc - however consider submitting the entire view worth of data. It's a lot of research you you may settle with what you have now. For example your business logic will be firing events on the form as it changes the form values (well, actually, they won't fire until the sub exits, due to the way VB deals with byref properties [=temp variable], but it needs to be considered).
You can also map an object's properties to a Form's properties with other libraries. Check out VALUE INJECTOR on the web. It can take a webform/winform and maybe even a WPF form and map the control values to and from an object you have predefined. Excellent for complete separation.
Hi well not sure about VB.NET but I know how do it in C#, therefore I did the solution in C# and then i used a C# to VB.NET convertor to get the VB.NET code. maybe it might help you:
http://www.developerfusion.com/tools/convert/csharp-to-vb/
C# Code
protected void TestFoo()
{
string strFooA = string.Empty;
GetFoo(ref strFooA);
Response.Write(strFooA);
}
private void GetFoo(ref string strFooA)
{
strFooA = "FooA";
}
VB.NET converted
Protected Sub TestFoo()
Dim strFooA As String = String.Empty
GetFoo(strFooA)
Response.Write(strFooA)
End Sub
Private Sub GetFoo(ByRef strFooA As String)
strFooA = "FooA"
End Sub
Pointers don't exist in the .NET framework like they do in C/C++, so you won't be able to directly achieve what you want.
There are some possible solutions on this SO page
See #NathanW answer on there:
If you are using VB the only thing that is really close to a pointer is a IntPtr. If you have access to C# you can use unsafe C# code to do pointer work.
In Addition
If you want to be able to use references then you can achieve this by using a class:
Private Class RefClass
Public x As Integer
End Class
Then you can reference one from the other:
Dim bar As New RefClass
Dim foo As RefClass = bar
bar.x = 45
Debug.WriteLine(bar.x) 'outputs 45
Debug.WriteLine(foo.x) 'outputs 45
You may already know enough to get the job done from the info at http://www.dreamincode.net/forums/topic/135354-reference-types-value-types-byval-byref/ ; however, your remark "ByRef is not required in declaration of local variables (and possibly class variables)" left me wondering if there's some confusion here.
For starters, the C++ example above, int x; int& foo = x; confuses what is meant by a "reference". My C++ isn't very strong, but, semantically speaking, I believe this C++ reference operates more like an aliasing mechanism. In VB (and C#) a reference operates like an identification code that locates an instance of a class in memory. Neither VB or C# has anything like the C++ reference type.
By now, you probably already know you can pass in your Form as a ByVal parameter, and change its properties and fields without problem. (ByVal is the default in VB.NET, so you don't even need to use it - in VB6 ByRef was the default.) If you're happy enough, you can skip the rest. But, yeah, in the case of .NET Reference variables, assuming:
Dim objA as New MyClass()
Dim objB as MyClass = objA
Then objA and objB both reference the very same instance of MyClass. You can modify via objA and objB, and you can read back from either objA or objB, because they each affect the very same instance. You can pass either objA or objB into some subroutine Foo with parameter objC As Object passed ByVal (i.e. Sub Foo(ByVal objC As Object) ) and Foo can then modify that same instance too.
The ByRef of VB and the ref of C# indicate a modifiable parameter, which means some "identification code" reference is passed instead of a value. Yet this ByVal vs. ByRef thing is clear as mud because in .NET there is a distinction made between "Value" types and "Reference" types that confuses many on whether a ByRef or ref is needed or not.
Visual Basic and C# dichotomizes variables (and data types) into two species: the "Value" (or "Structure"), and the "Reference" (or "Class").
The "Value" type means an actual collection of bits that represents an Integer, or a Boolean, or even a bitmap, or some other kind of object. In old school parlance, this is the "image" of the instantiation of an object. It is the state space of the object. It is what makes an object essentially itself, independent of where in memory it may be.
The "Reference" type means a code (which might look like an integer or a pointer) that somehow indicates the data type of the object and where in memory it resides. The computer will interpret a "Reference" to obtain the actual image of the object (i.e. its "Value").
When a "Value" parameter is passed ByVal, that means a new object is created that is in the identical image of the original expression being passed, and it is upon this copy that the function or method operates. The original image of the "Value" cannot be affected.
When a "Value" parameter is passed ByRef, that means a new "Reference" variable is created, and that "Reference" variable will contain the information that will interpret back to the image of the original "Value". Now the original image of the "Value" can be changed.
When a "Reference" parameter is passed ByVal, its "identification code", which gets interpreted back to the actual image of the object, gets copied. It is upon this copy of the code that the function or subroutine or method operates. This copy still points to the actual image of the object. Which means that an object of a Reference variable that is passed by ByVal can still have its image (i.e. its "Value") changed. However, the code of the original "Reference" itself cannot be changed.
(Note that the String type is an odd duck: It will behave as if it were a "Value" parameter even though it is in fact a "Reference" type. Hence a String passed ByVal will not be affected in the same way any other class would. Actually, String is an example of an immutable type - which means that steps are taken to prevent changes to its "Value".)
When a "Reference" parameter is passed ByRef, one now has created a new "Reference" object that points to the original "Reference" object (that, in turn, points to the "Value" of some other object via its "identification code"). The use of ByRef on a "Reference" allows one to modify (or create anew) the "identification code" of the original "Reference" object being passed as a parameter. A function or subroutine or method that performs a swap operation will use ByRef on "Reference" parameters.
Does VB.NET have a direct equivalent to C# out function parameters, where the variable passed into a function does not need to be initialised?
No, there is no equivalent of the out keyword in VB.
However, VB does automatically initialise all local variables in a method, so you can use ByRef without needing to explicitly initialise the variable first.
Example:
Sub Main()
Dim y As Integer
Test(y)
End Sub
Sub Test(ByRef x As Integer)
x = 42
End Sub
(If you examine code in the framework (for example Double.TryParse), you may see the <OutAttribute> added to parameters, but that only makes a difference when the call is marshalled for COM interop or platform invoke.)
No, there is no equivalent construct that allows a non-initialised variable to be passed to a method without a warning, but, as mentioned in my question and answer specifying an <Out()> attribute on a ByRef parameter definition, although VB ignores it, is treated by C# as an out parameter.
So, I would pre-initialise reference variables to Nothing and specify <Out()> ByRef to signify the intention (that will work if C# users ever access your methods).
If you feel you know when you intend to access the default Nothing in otherwise unassigned reference variables you can set the "Warning configuration" "Use of variable prior to assignment" to "None" at the Project level (Project Properties > Compile, and you probably want to set Configuration to "All Configurations" before changing this setting), or, in VS2015 (VB.NET 14), you can use #Disable Warning BC42030.
C# version
void TestFunc(int x, ref int y, out int z) {
x++;
y++;
z = 5;
}
Vb.net version
Sub TestFunc(ByVal x As Integer, ByRef y As Integer, ByRef z As Integer)
x += 1
y += 1
z = 5
End Sub
Found the answer here
Update
As stated in the comment do not forget to initialze your parameter that will be used in the out slot
I had the problem in VB.NET that I called a function "by ref" that passed an array back.
Even though the compiler flagged it as a warning it was fine. The fix is super simple and probably good programming practice.
I changed
Dim m_arr_values() as Integer
fnRetArray(m_arr_values)
to
' Even though 'Nothing' is the default value, setting it
' stops the compiler complaining.
Dim m_arr_values() as Integer = Nothing
fnRetArray(m_arr_values)
It also helps when coding if variable names are descriptive...
Sub fnCreatePalette(ByRef arr_in_pal() As color, ByRef arr_out_pal() as uinteger)
...
End Sub
VB has the attribute which should be the same as C# out but today you still get a warning even if you use it. There are details about fixing it in vblang area of github. https://github.com/dotnet/vblang/issues/67.
You can use the pass by reference method in VB.NET.
You need the Out parameter mechanism in C#, because it doesn't let you use any variable without initializing it.
VB.NET doesn't need a special keyword as it automatically does it by itself.
Just use ByRef.
Use keyword ByRef before variable.
Below I tried to do an Example:
Public Function UserData(ByVal UserDN As String) As DataTable
Dim myTable As DataTable = UserData_Table()
Dim dr As DataRow
dr = myTable.NewRow()
SplitOU2(UserDN, dr("OUDN"), dr("Organisation"), New Object)
dr("UserDN") = UserDN
myTable.Rows.Add(dr)
Return myTable
End Function
Below is the called method:
Friend Sub SplitOU2(ByVal inDN As String, ByRef OUDN As Object, ByRef Organisation As Object, ByRef VerksamhetTyp As Object)
By doing this I can skip to declare the in this example "useless" variable
Dim VerksamhetTyp as Object = "".
Perhaps it looks a little ugly but to have to declare unused variables can also be confusing.
Summary: Check whether or not the method really needs those parameters to be ByRef. Also check that you really don't care about anything it does to the parameters. After scrupulous checking, it's okay to do this - nothing "bad" will happen in terms of the CLR, because it's just a compiler trick under the hood.
Well, VB (unlike C#) will let you do this. Behind the scenes it's effectively creating a new variable and passing it by reference - after all, it has to for the method to be called properly. However, I'd say this is usually a bad idea. The point of ByRef is that you use the value after it's been set within the method.
Do you really need all those parameters to be ByRef in the first place? If you find yourself doing this a lot for a particular method, you could always write a wrapper method which called the original one, but didn't have the ByRef parameters itself.
(I usually find that methods with a lot of ByRef parameters indicate either a lack of understanding of reference types in .NET, or that the parameters should be encapsulated in their own type.)
Having said all of this, it's not always incorrect to ignore the value of a ByRef argument after calling the method. For example, if you just want to know whether or not some text can be parsed as an integer, then using Int32.TryParse is reasonable - but only the return value is useful to you.
The reason that I consider to use this has to do with that the method has even more parameters and that different operation overloads gets the same signature ….
The fact that it works is quite fun and somthing I became awarae óff by chance ...