Search code examples
c#vb.net.net-2.0vb.net-to-c#

Why does my code compile in VB.NET but the equivalent in C# fails


The following VB.NET code works:

Dim request As Model.LearnerLogbookReportRequest = New Model.LearnerLogbookReportRequest
request.LearnerIdentityID = Convert.ToInt32(Session("identityID"))
request.EntryVersion = LearnerLogbookEntryVersion.Full

Dim reportRequestService As IReportRequestService = ServiceFactory.GetReportRequestService(ServiceInvoker.LearnerLogbook)
        reportRequestservice.SaveRequest(request)

The following C# code fails to compile:

LearnerLogbookReportRequest request = new LearnerLogbookReportRequest();
request.LearnerIdentityID = theLearner.ID;
request.EntryVersion = LearnerLogbookEntryVersion.Full;

IReportRequestService reportRequestService = ServiceFactory.GetReportRequestService(ServiceInvoker.LearnerLogbook);

reportRequestService.SaveRequest(ref request);

The LearnerLogbookReportRequest is declared as:

Public Class LearnerLogbookReportRequest
    Inherits AbstractReportRequest

Error:

Error   11  Argument 1: cannot convert from 'ref RACQ.ReportService.Common.Model.LearnerLogbookReportRequest' to 'ref RACQ.ReportService.Common.Model.AbstractReportRequest'    C:\p4projects\WEB_DEVELOPMENT\SECURE_ASPX\main-dev-codelines\LogbookSolution-DR6535\RACQ.Logbook.Web\Restful\SendLogbook.cs 64  50  RACQ.Logbook.Web

Why is the C# version failing to compile?


Solution

  • VB is rather looser with ByRef parameters than C# is. For example, it allows you to pass properties by reference. C# doesn't allow this.

    In a similar way, with Option Strict off, VB allows you to use an argument which is a subtype of the declared parameter. As a short but complete program, consider this:

    Imports System
    
    Public Class Test
        Public Shared Sub Main(args As String())
            Dim p As String = "Original"
            Foo(p)
            Console.WriteLine(p)
        End Sub
    
        Public Shared Sub Foo(ByRef p As Object)
            p = "Changed"
        End Sub
    End Class
    

    That works in VB, but the equivalent in C# wouldn't... and for good reason. It's dangerous. In this case, we're using a string variable and we happen to change p to refer to another string, but if we change the body of Foo to:

    p = new Object()
    

    Then we get an exception at execution time:

    Unhandled Exception: System.InvalidCastException: Conversion from type 'Object' to type 'String' is not valid.

    Basically ref is compile-time type-safe in C#, but ByRef isn't type-safe in VB with Option Strict off.

    If you add:

    Option Strict On
    

    to the program in VB, however (or just change the defaults for your project) you should see the same problem in VB:

    error BC32029: Option Strict On disallows narrowing from type 'Object' to type
    'String' in copying the value of 'ByRef' parameter 'p' back to the matching
    argument.
    
            Foo(p)
                ~
    

    This suggests that you're currently coding without Option Strict... I'd recommend using it ASAP.