I think I'm missing something fundamental about how DataTables work. The following procedure starts with GetData, bringing in what as a filled DataTable. All the following code does is pass through copies of the datatable, manipulate it, then return it:
Sub GetData(ByVal what As DataTable)
Dim Part As DataTable = Generate(what)
End Sub
Function Generate(ByVal brown As DataTable)
Dim lameface As DataTable = DoStuff(brown)
Return lameface
End Function
Function DoStuff(ByVal cow As DataTable)
Dim result As DataTable = cow
result.Rows.RemoveAt(0)
Return result
End Function
The way this is written above, function DoStuff will remove the top row from result and cow. Similarly, brown and what will also have that first row removed, even though they are sent as ByVal.
If I change the first line in DoStuff from
Dim result As DataTable = cow
to
Dim result As DataTable = cow.copy
then cow, brown and what are left alone. Why is this? Marking a parameter as ByVal is supposed to send a copy of the object instead of the original, so why do I have tell it to use a copy when instantiating result? If I do a similar procedure using integers instead of datatables, it works as I would expect. What am I missing about datatables?
I poked around on MSDN's articles for datatables and didn't see anything that spoke to this. Thanks for your help!
The ByVal keyword doesn't necessarily indicate that a copy of the value is passed to the method. If the parameter is a reference type, like a DataTable, then a copy of the pointer is passed to the method - it still references the same object, so any changes made to the object in the method will be maintained once the method completes execution.
The ByRef keyword would permit the method to change the object the variable is pointing to, or the actual value if the parameter is a simple type (like int).
In your situation above, if you want to remove a row inside DoStuff, but not affect the source DataTable, then you need to make a copy of the DataTable before doing the remove operation.
Related
**I hav problem when i use DataGridView1 with thread
i get empty rows like image **
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thread As New Thread(AddressOf grid)
thread.Start()
End Sub
Public Function grid()
For i As Int16 = 1 To 50
Invoke(Sub()
DataGridView1.Rows.Add(i)
DataGridView1.Refresh()
End Sub)
Next
End Function
https://i.stack.imgur.com/KOXRR.jpg
This is a perfect example of why you MUST read the relevant documentation, especially if something doesn't work the way you expect. If you had bothered to read the documentation for the Add method you're calling here then you'd have seen that that particular overload adds the specific number of rows to the grid and that's all. At no point does it populate those rows with any data. The code you have is going to add 1+2+3+4+...+48+49+50 empty rows to your grid. If what you actually want is to add a single row each time where i is the data displayed in that row then you need to call one of the overloads of Add that does that, or else call the overload with no arguments to add a single row, then get that row and set the Value of a cell explicitly.
EDIT: The simplest way to add a row with i as data is to call the overload of Add that takes an array of cell values. If you only have one cell, you use an array with one element:
DataGridView1.Rows.Add({i})
The braces indicate that the contents is an array. It's shorthand for this:
DataGridView1.Rows.Add(New Integer() {i})
Note that, if you pass a single Integer, the overload with a single Integer parameter will be called as it's the closest match. If you were to pass multiple discrete Integers, e.g.
DataGridView1.Rows.Add(i, i)
then you would not have the same problem because there is no overload with multiple Integer parameters. In that case, the overload that takes an array of Objects would be called. That overload's parameter is declared ParamArray, which means that you can pass either multiple individual objects or a single array. Another option would be to cast your Integer as type Object, which would also cause the overload that takes an array of Objects to be called:
DataGridView1.Rows.Add(CObj(i))
When you call an overloaded method, the closest valid match will be called, so be sure that your arguments match the parameters of the overload you want to call and make sure you understand what each overload does. ALWAYS read the documentation if you aren't 100% sure. The F1 key and the Help menu are there for a reason.
This question already has answers here:
Argument passed ByVal to VB.NET Function and manipulated there
(2 answers)
Closed 4 years ago.
The list value changeed here when passed ByVal why
,it must be not changed.
Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
Dim value As Integer = 1
Dim value2 As New List(Of Decimal)
value2.Add(1)
value2.Add(2)
' The integer value doesn't change here when passed ByVal.
Example1(value)
Console.WriteLine(value)
' The list value changeed here when passed ByVal.
Example3(value2)
Console.WriteLine(value)
End Sub
Sub Example1(ByVal test As Integer)
test = 10
End Sub
Sub Example3(ByVal test As List(Of Decimal))
test.Add(3)
End Sub
its solved ,the solution is making new copy:
Sub Example3(ByVal test As List(Of Decimal))
Dim testnew As New List(Of Decimal)
testnew.AddRange(test)
testnew.Add(3)
End Sub
You need to do some reading on value types and reference types and passing method arguments by value and by reference. They are related but not the same thing. When you pass a method argument by value, you create a copy of the variable being passed. If the variable is a value type, i.e. a structure, then that means creating a copy of the value. If the variable is a reference type, i.e. a class, then that means creating a copy of the reference. The thing is, the original reference and the copy still both refer to the same object.
The reason that reference types exist is that you wouldn't want to create copies of large objects every time you assigned them somewhere. In the case of passing a collection to a method, it's almost always the case that any change you make inside the method you will want to be reflected outside. In the rare case that you don't, it's up to you to create a copy of the collection first and pass that in.
When you pass a value type by value, you create a copy of the value. That means that no changes you make inside the method can affect the original variable. You can assign a new value to the parameter or you can set a property of the value and the change will not be reflected outside the method. Of course, value types should generally be immutable and so setting a property should not be possible, but there are times that that "rule" gets broken.
When you pass a reference type by value, you create a copy of the reference. That means that assigning a different object to the parameter inside the method will not affect the original variable. There is still only one object though, referred to by the original variable and the parameter. As such, if you set a property of that object via the parameter then that change will be reflected in the original variable, because it's the same object.
When you pass a value type by reference, you create a new reference to the value. That means that any changes you make inside the method will affect the original variable. You can assign a new value to the parameter or you can set a property of the value and the change will be reflected outside the method.
When you pass a reference type by reference, you create a new reference to the original reference. That means that assigning a different object to the parameter inside the method will affect the original variable. There's still just one object, so setting a property on the parameter will still affect the original variable too.
Those are the only four possibilities: value type by value, reference type by value, value type by reference and reference type by reference. In none of those scenarios is a copy of a reference type object made so in none of those scenarios can you set a property of a reference type object via a method parameter and have that change not be reflected in the original variable.
If you want a copy of the original object then it's up to you to create one explicitly. Whether you do that inside the method or outside really depends on the specific circumstances. That means that you need to change your code to this:
Sub Example3(ByVal test As List(Of Decimal))
Dim copy = test.ToList()
copy.Add(3)
End Sub
or this:
Dim copy = value2.ToList()
Example3(copy)
Let me just repeat the important point here: there is NO WAY to pass a reference type object to a method, modify the object via the parameter inside the method and have that not affect the original variable (assigning a different object to the parameter is NOT modifying the object). If you want a modification inside the method to not affect the original variable then you need a copy of the object and the ONLY way that will happen is if YOU do it explicitly.
I've hit a snag, and my searching hasn't helped so far.
I have a variant being passed into a function which I then intend to copy, perform some calculations, take another copy, perform some other calculations then compare the results of the two copies...
However, when I perform the calculations on one copy, the original variant is also manipulated... so after two copies, and two calculations I end up with 3 variants that are equal to each other and different to the original... Not what I intended.
I expect this is happening because when I use NewVar = OldVar I'm actually taking a reference to the original object. What I actually want, is to make an independent duplicate of the original object - i.e. copy the value of the variable similar to byval in a function delcaration.
My code is linked here: https://1drv.ms/x/s!AiPgb0BH-YZ_ga956eMmJbSdihGjyg.
If you put a break on line 67 of modMain, then watch CutList(1).QTY (the original variable), and CutTrial.RemainingCuts(1).QTY you'll see that both the QTY values decrement when you step through line 67... I want CutList(1).QTY to remain unchanged, and CutTrial.RemainingCuts(1).QTY to decement only.
Any suggestions?
Make sure the functions definition is as follows
Public function DoMagic(ByVal variable as something)
Don't use ByRef or as you found it will modify the reference.
If you are using an object, array or collection you will need to first copy it before using it.
Eg something like the following:
Public Function Clone() As Class1
Set Clone = New Class1
Clone.prop1 = prop1
Clone.PrivateThing = PrivateThing
End Function
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.
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 ...