5

According to the MSDN entry for Nothing (Visual Basic)

Nothing represents the default value of a data type.

It has also been noted by some that "...the Nothing keyword is actually equivalent to C#’s default(T) keyword".

This has been giving me some anomalous behaviour in a multi-language solution I have been working on recently. Specifically I have been getting with more than a few TargetInvocationExceptions being thrown at the C# end when the VB.NET async methods return Nothing.

Is it possible to set a variable in the VB.NET projects to C#'s null and be able to test for this null value in both C# and VB.NET.


Here is a snippet that is not behaving as expected. The C# project imports the VB.NET project as a reference.

VB.NET Side

Public Function DoSomething() As Task(Of Object)
    Dim tcs = New TaskCompletionSource(Of Object)
    Dim params = Tuple.Create("parameters", tcs)

    AnotherMethod(params)

    Return tcs.Task
End Function

Public Sub AnotherMethod(params As Tuple(Of String, TaskCompletionSource(Of Object))
    ' do some activities
    If result = "Success" Then
        params.Item2.SetResult("we were successful") ' result can also be of different type
    Else
        params.Item2.SetResult(Nothing)  ' could this be the source of the exception?
    End If
End Sub

C# Side

public async void AwaitSomething1()
{
    var result = "";
    result = (await DoSomething()).ToString(); // fails if Result is Nothing
}

public async void AwaitSomething2()
{
    var result = "";
    result = (string)(await DoSomething());    // fails if Result is Nothing
}

public async void AwaitSomething3()
{
    var task = DoSomething();
    await task;                                // also fails if Result is Nothing
}

There is no exception thrown when VB.NET's AnotherMethod is successful. However, when it is not successful and tcs's result is set to Nothing, everything falls on its head.

How can I effectively SetResult to Nothing without resulting in an exception or, otherwise, how can I SetResult to C#'s null?

Alex Essilfie
  • 12,339
  • 9
  • 70
  • 108
  • You mean you get an exception on the `ToString()` call because the return value of `await DoSomething()` is null? – lc. Aug 21 '15 at 04:30
  • 5
    i could be wrong, but on the c# side it looks like your trying to pass a null value to the `.ToString()` Which could result in a "null value" error being thrown, alternativly give us the erorr your getting "falls on it's head" isn't as informative as "system.whatever returned a value of X when it expected Y" – Nikerym Aug 21 '15 at 04:30
  • 1
    What is the exception's `InnerException` ? – Will Aug 21 '15 at 04:35
  • 1
    The exception obtained is a `TargetInvocationException` with the message "A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll". Visual Studio displays the Source Not Available page and there are no stack traces available either. – Alex Essilfie Aug 21 '15 at 04:36
  • Side note: You have single class that mixes C# and VB implementations? Never seen such thing... – Alexei Levenkov Aug 21 '15 at 05:01
  • @AlexeiLevenkov: No, that's not so. Like I stated in the question, it is a Visual Studio Solution which contains C# and VB.NET projects. – Alex Essilfie Aug 21 '15 at 05:04
  • `SetResult` isn't failing because of the `Nothing`, is it? Can you also try it without `async`? And is calling `tcs.Task` valid after calling `tcs.SetResult(Nothing)` ? – Rob Aug 21 '15 at 05:13
  • @Rob: The actual implementation prevents running the code synchronously. Since it is asynchronous I can't break to inspect `tcs.Task`. It would have already been returned to the C# code which would be awaiting a result. – Alex Essilfie Aug 21 '15 at 05:34
  • @AlexEssilfie Can you just stub out the actual implementation then, and pretend it fails? I highly doubt it's failing because of `Nothing` -> `null` - that's going to happen in almost any application with both C# and VB – Rob Aug 21 '15 at 05:35
  • 1
    Your third example doesn't make sense to me. There's no reason that `await task;` should fail, even if the result value is null. The `DoSomething()` method always returns a non-null object, so `task` itself is always non-null, and so `await task` should always be valid. The result is null, but you never bother to use it in that third example, so it being null is not a problem. Other than that, [the answer from un-lucky](https://stackoverflow.com/a/32132989) addresses your concerns: there's not a problem with `Nothing` vs `null`...you wouldn't be allowed to dereference `Nothing` in VB either! – Peter Duniho Aug 21 '15 at 05:43
  • 1
    `.ToString()` method or `await()` is not capable of handling null that's the reason for the exception you are getting. – sujith karivelil Aug 21 '15 at 05:51
  • @PeterDuniho: Sorry. I did too much redaction of the code while pasting it here. And you were right. The result is indeed `null` but I forgot to check it before trying to assign it to a variable expecting a `string`. – Alex Essilfie Aug 21 '15 at 07:17

3 Answers3

2

This is not because of conversion from Nothing to null. Here i have some example for you where C# accepts Nothing as null:

Vb Class Library Code:

Public Class ClassVb
    Public Function Dosomething() As Task(Of Object)
        Return Nothing
    End Function
End Class

C# that Invoke This Class Library :

using vbclassLib;
  class Program
    {
     static void Main(string[] args)
        {
            ClassVb classLibObj = new ClassVb();
            var result = classLibObj.Dosomething();//result=null
        }
    } 

Which works fine and gives result=null, ie., Nothing is converted as null

Let me come to your scenario:

In your scenario, when the function returns Nothing its definitely converted to null but .ToString() method or await() is not capable of handling null that's the reason for the exception you are getting.

  • null.ToString() or (null).ToString() says that The operator '.' cannot be applied to operand of type '<null>'.

  • await(null) will not be allowed by c#, it says cannot await null.

This may help you:

ClassVb classLibObj = new ClassVb();
var temp = classLibObj.Dosomething();
var result = temp == null ? "" : temp.ToString();  
sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
  • 1
    The whole `This may help you` part can be written `var result = (new ClassVb().DoSomething() ?? "").ToString();` – Arthur Rey Aug 21 '15 at 09:06
0

I've seen a related issue resolved by having the VB project cast 'Nothing' to the 'Object' type to ensure that it is the default value corresponding to reference types (e.g., C# null) and received by the C# project as such, instead of the default value of some value type (e.g., 0 for an integer type):

params.Item2.SetResult(CObj(Nothing))
Dave Doknjas
  • 6,394
  • 1
  • 15
  • 28
0

My error became apparent after reading un-lucky's statement:

  • null.ToString() or (null).ToString() says that The operator '.' cannot be applied to operand of type '<null>'.
  • await(null) will not be allowed by c#, it says cannot await null.

It turns out that because I had set the result variable in the C# code to string, it was not possible to convert from Object (as in the VB.NET's return type of Task(Of Object)) without having to do a .ToString() or (string)(await Method()). Using either of these two procedures also result in a NullReferenceException.

I was finally able to figure out what I was doing wrong by first assigning the Task<object> returned to a variable and then checking its result for null after awaiting. Thus, I finished up with code like this which suits my purpose.

public class CSharpClass
{
    public async void AwaitSomething()
    {
        var task = new VbNetClass().DoSomething();
        await task;
        // test task.Result for null
        var result = (task.Result ?? "method was unsuccessful").ToString();

        // rest of code follows
    }
}

Thanks to nikerym and Peter Duniho for their contribution. I was too tired from staying up for over 20 hours to notice the point they were making. What beats me though is why I had a TargetInvokationException instead of a NullReferenceException.

Community
  • 1
  • 1
Alex Essilfie
  • 12,339
  • 9
  • 70
  • 108
  • You get `TargetInvocationException` because of the way `await` works: only the code up to the first `await` is actually in the named method where you've declared the code. The rest of the code is put into continuations (actually, a single method encapsulating a state machine), which are invoked on completion of the awaited task. If you had looked at the inner exception of the `TargetInvocationException`, you'd have seen the `NullReferenceException` that actually described what was going wrong. – Peter Duniho Aug 21 '15 at 15:35