0

I am in the midst of converting a VB library to C#. One of my methods has the following signature in VB:

Private Shared Sub FillOrder(ByVal row As DataRowView, ByRef o As Order)

In C# I converted it to:

private static void FillOrder(DataRowView row, ref Order o)

From my constructor inside my Order class, I am calling the FillOrder() method like so:

DataView dv = //[get the data]
if (dv.Count > 0)
{
    FillOrder(dv[0], this);
}

In VB, this works:

Dim dv As DataView = '[get data]'
If dv.Count > 0 Then
    FillOrder(dv.Item(0), Me)
End If

However, in VS10 in the C# file I am getting a red squiggle under this call with the following error:

The best overloaded method match for [the method] has some invalid arguments

This was working code in VB. What am I doing wrong?

Jason
  • 51,583
  • 38
  • 133
  • 185
  • I wonder how could it work: `this` should be readonly, right? So you shouldn't be able to have `ref this`. – Vlad Dec 22 '10 at 20:40
  • In C# you don't need to add the `ref` keyword for the `Order` parameter. It is a reference type and you probably don't need to pass it by ref. – Oded Dec 22 '10 at 20:41
  • @Oded: What? There's no difference between `ref` and `ByRef`. – SLaks Dec 22 '10 at 20:42
  • Unless you can give a good explanation, you shouldn't be using a `ref` parameter in the first place. – SLaks Dec 22 '10 at 20:42
  • @slaks what is wrong w/using a `ref` param? – Jason Dec 22 '10 at 20:43
  • @Jason: `ref this` is bad, `this` is read-only. – Vlad Dec 22 '10 at 20:50
  • No, you (probably) don't need a `ref` parameter. Why do you think you need a `ref` parameter? – SLaks Dec 22 '10 at 20:51
  • passing using `ref` means that the method can change what the variable passed in is pointing to. That doesn't make a lot of sense in this situation. Just remove `ref` and you should be good. – Bryan Dec 22 '10 at 20:52

3 Answers3

5

You need to pass the second parameter as ref.
However, you cannot pass this as ref (unless it's a struct), so you'll need a temporary variable.

Note that the parameter almost definitely shouldn't be ref in the first place.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
2

According to the documentation, ByRef in VB for reference types is not the same as ref in C#. It rather means that the function may change the variable.

So, just drop ref from the function definition:

private static void FillOrder(DataRowView row, Order o)

BTW, is your Order a class or a struct?

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • `Order` is a class. I need the `ref` call, so I overloaded `FillOrder()` and got everything working correctly. Thank you for your help! – Jason Dec 22 '10 at 20:51
  • **Wrong**. `ByRef` and `ref` are the same. – SLaks Dec 22 '10 at 20:53
  • @Jason: why do you need `ref`? Do you replace `o` inside of the function? – Vlad Dec 22 '10 at 20:53
  • @SLaks: **proof**? (If they really were the same, one couldn't pass `this` as `ByRef`.) – Vlad Dec 22 '10 at 20:54
  • @Vlad: VB degrades to pass-by-value where necessary. http://blogs.msdn.com/b/vbteam/archive/2010/01/26/the-many-cases-of-byref.aspx – SLaks Dec 22 '10 at 22:42
  • @SLaks: didn't find exactly the discussed case at the article, but: the article confirms that the perceivable semantics of `ByRef` is not the same as that of `ref`. – Vlad Dec 22 '10 at 23:22
  • @Vlad: This is an example of `Don’t Copy Back ByRef`. No. This behavior is at the call site; it's a difference between C# and VB. The behavior will be the same whether you're pass a `ByRef` parameter or a `ref` parameter. – SLaks Dec 22 '10 at 23:27
  • @SLaks: (1) Now I see the example, thanks. (2) The fact that the behaviour of call site is different is a pure implementation detail. The semantics of ByRef in VB consists of both how the function behaves and what the call statement is doing (this is true not only for this specific case, right? consider C++'s reference parameters, which are translated to a pointer by the call site). So the semantics is different. And this is the reason why replacing `ByRef` in VB code with `ref` in C# code didn't work. Would the semantics be the same, the problem wouldn't arise at all. – Vlad Dec 22 '10 at 23:35
  • @Vlad: The semantics of the _argument_ aren't different; they both compile to identical IL. If you pass a `ByRef` parameter in C#, you get C# semantics. – SLaks Dec 22 '10 at 23:39
  • @SLaks: the IL is again an implementation detail, it's only the observable behaviour what defines the semantics. – Vlad Dec 22 '10 at 23:40
  • The observable behaviors depends on the language of the callsite. – SLaks Dec 22 '10 at 23:42
  • Exactly. They depend on the language of the _callsite_, not of the function. – SLaks Dec 22 '10 at 23:46
  • @SLaks: And this is basically what I am saying from the very beginning. The behaviour of `ByRef` in VB is different from the behaviour of `ref` in C#. Which exactly parts of the both languages are responsible for the difference--is the function different, or the call is generated in a different way--is not really relevant to the question (although is an interesting implementation detail). – Vlad Dec 22 '10 at 23:50
1

As long as I can remember, there has always been confusion about ByVal and ByRef/ref parameters. Here's the best way I can explain it:

You only need to pass an object by reference if and only if you plan on replacing that reference with a different reference. If you want to change the contents of the object being passed, you only need to pass it by value. Example:

Public Class Person
    Public Property FirstName As String
    Public Property LastName As String
End Class

Public Shared Sub ModifyPerson(ByVal someone As Person)
    ' Passed by value          ^^^^^

    someone.LastName = "Doe"
End Sub

Public Shared Sub Main(ByVal args() As String)
    Dim me As New Person
    me.FirstName = "Adam"
    me.LastName = "Maras"

    ModifyPerson(me)

    Console.WriteLine(me.LastName) ' Writes "Doe"
End Sub

Yes, the instance of Person called me is passed into ModifyPerson by value; that just means the reference to the instance is passed by value. A function can still modify the members of that reference. Now, try this:

Public Shared Sub Main(ByVal args() As String)
    Dim me As New Person
    me.FirstName = "Adam"
    me.LastName = "Maras"

    AssignByValue(me)
    Console.WriteLine(me.LastName) ' Writes "Maras"

    AssignByReference(me)
    Console.WriteLine(me.LastName) ' Writes "Doe"
End Sub

Public Shared Sub AssignByValue(ByVal someone As Person)
    Dim new As New Person
    new.FirstName = "John"
    new.LastName = "Doe"

    someone = new
End Sub

Public Shared Sub AssignByReference(ByRef someone As Person)
    Dim new As New Person
    new.FirstName = "John"
    new.LastName = "Doe"

    someone = new
End Sub

These functions differ because they try to modify the actual reference being passed in. AssignByValue has no effect on the Person named me because the parameter is passed by value. However, AssignByReference can change the value of that parameter in the method that called it, hence why the second call to Console.WriteLine(me.LastName) reflects the updated reference.

Adam Maras
  • 26,269
  • 6
  • 65
  • 91