vblang icon indicating copy to clipboard operation
vblang copied to clipboard

VB.NET does not allow defining a Private `WriteOnly` property with the same name as a Public `ReadOnly` property. This causes issues when trying to encapsulate state management in a nice VB.NET way.

Open Arry-eng opened this issue 9 months ago • 6 comments

Bug Description

Bug: Property defination overload with Public and Private visibility and overridden with ReadOnly and WriteOnly access modifier is throwing BC30301, BC30366, BC32429 and BC37243

Steps to Reproduce

  1. Create a VB.NET module.
  2. Define a Public ReadOnly property and a Private WriteOnly property with the same name.
  3. Observe the compiler error.

Expected Behavior

The compiler should allow Public ReadOnly and Private WriteOnly properties with the same name, as they serve different purposes.

Actual Behavior

The compiler throws the below errors:

  • Error (active) BC30301 'Public ReadOnly Property IsResponseReady As Boolean' and 'Private WriteOnly Property IsResponseReady As Object' cannot overload each other because they differ only by return types. YYYY XXX.vb 2
  • Error (active) BC30366 'Public ReadOnly Property IsResponseReady As Boolean' and 'Private WriteOnly Property IsResponseReady As Object' cannot overload each other because they differ only by 'ReadOnly' or 'WriteOnly'. YYY XXX.vb 2
  • Error (active) BC31429 '_isResponseReady' is ambiguous because multiple kinds of members with this name exist in module 'ModuleZ'. YYY XXX.vb 4
  • Error (active) BC37243 Auto-implemented properties cannot be WriteOnly. YYY XXX.vb 8

Code Example

Below is are code snippets which is reporting errors:

Private _isResponseReady As Boolean = False   ' -Should be initially False to avoid Command/Response dialog Error
Public ReadOnly Property IsResponseReady As Boolean
    Get
        Return _isResponseReady
    End Get
End Property

Private WriteOnly Property IsResponseReady  '//Bug: VB.NET does Not allow such overloaded functions
    Set(value As Boolean)
        _isResponseReady = value
    End Set
End Property

Current Remedy

Define Private methods 'Set+property name' to set the value of the property.

Code Example With Remedy

The remedy I adopted is as below. A slightly bigger block of code is included to show the context of the usage.

'// Define events for synchronization
 Public Event CommandIssued()
 Public Event ResponseRead()

 Private _isResponseReady As Boolean = False '' Should be initially False to avoid Command/Response dialog Error
 Public ReadOnly Property IsResponseReady As Boolean
     Get
         Return _isResponseReady
     End Get
 End Property

 'Private WriteOnly Property IsResponseReady //Bug: VB.NET does Not allow such overloaded functions
 '    Set(value As Boolean)
 '        _isResponseReady = value
 '    End Set
 'End Property

 Private Sub SetResponseReady() ''Current remedy for the above bug
     _isResponseReady = IsCommandReady
 End Sub

 Public ReadOnly Property IsCommandReady As Boolean
     Get
         Return Not IsResponseReady
     End Get
 End Property

 Private Sub SetCommandReady()
     _isResponseReady = IsCommandReady
 End Sub

Friend Sub OnCommandIssued()
     Debug.Assert(IsCommandReady, "Command/Response dialog Error. CommandIssued() Event should have a preceding ResponseRead() Event in one-to-one dialog relationship")
     SetResponseReady()
     '' IsResponseReady = True ''_isResponseReady = True // Bug :  VB.NET does Not allow such overloaded functions
     ' Additional logic for when a command is issued
 End Sub

Friend Sub OnResponseRead()
     Debug.Assert(IsResponseReady, "Command/Response dialog Error. ResponseRead() Event should have a preceding CommandIssued() Event in one-to-one dialog relationship")
     SetCommandReady()
     ''IsCommandReady = True ''_isResponseReady = False IsResponseReady = False // Bug : VB.NET does Not allow such overloaded functions
     ' Additional logic for when a response is read
 End Sub

Environment

  • VB.NET Version: 9.0.200 Commit: 90e8b202f2
  • Target Framework: .NET 4.8.1 (or higher)
  • IDE: Visual Studio Community MSBuild version: 17.13.8+cbc39bea8
  • Workload version: 9.0.200-manifests.b6391994 donet--info.txt

Additional Context

This issue impacts scenarios where state needs to be encapsulated with separate read and write logic. Other Possible Scenarios:- I need to overload a Public ReadOnly Property declaration with a Private WriteOnly Property declaration in a derived 'downstream' Class implementation.

"Further details on the impact, priority, and justification for this feature can be found in the comments below."

Real Life Scenario - Hypothetical

Here is a code snippet of a hypothetical scenario which necessitates such property overloads and overrides

IndianGov.vb.txt

Module IndianGov

    ' Base class representing the Prime Minister's Office
    Public Class PMOffice
        ' Public ReadOnly property for Information
        Public Overridable ReadOnly Property Information As String
            Get
                Return "Confidential Information"
            End Get
        End Property

        ' Public WriteOnly property for Complaints
        Public Overridable WriteOnly Property Complaints As String
            Set(value As String)
                ' Default implementation for handling complaints
                Console.WriteLine("Complaint received: " & value)
            End Set
        End Property
    End Class

    ' Derived class representing the NSA
    Public Class NSA
        Inherits PMOffice

        ' Override Information as Private WriteOnly
        Private Overrides WriteOnly Property Information As String
            Set(value As String)
                ' Logic for handling information privately
                Console.WriteLine("NSA is handling information privately.")
            End Set
        End Property

        ' Override Complaints as Public WriteOnly
        Public Overrides WriteOnly Property Complaints As String
            Set(value As String)
                If value.Contains("Opposition") Then
                    ' Add to Information
                    Console.WriteLine("Adding complaint to Information: " & value)
                ElseIf value.Contains("CurrentParty") Then
                    ' Send to PartyHQ and execute private functions
                    Console.WriteLine("Sending complaint to PartyHQ: " & value)
                    ExtortionAndHarassmentOfComplainant()
                    MOSSAD.Engage.GetElectronicInfo(New List(Of String) From {"Phone1", "Phone2"},
                                                    New List(Of String) From {"Address1", "Address2"},
                                                    New List(Of String) From {"BankAccount1", "BankAccount2"},
                                                    New List(Of String) From {"Employer1", "Employer2"})
                End If
            End Set
        End Property

        ' Private function for extortion and harassment
        Private Sub ExtortionAndHarassmentOfComplainant()
            Console.WriteLine("Executing extortion and harassment of complainant.")
        End Sub
    End Class

    ' Derived class representing the Home Secretary
    Public Class HomeSecretary
        Inherits PMOffice

        ' Override Information as Private WriteOnly
        Private Overrides WriteOnly Property Information As String
            Set(value As String)
                ' Logic for handling information privately
                Console.WriteLine("Home Secretary is handling information privately.")
            End Set
        End Property

        ' Override Complaints as Public WriteOnly
        Public Overrides WriteOnly Property Complaints As String
            Set(value As String)
                If value.Contains("ED Raid on Opposition Employee") Then
                    ' Add to Information
                    Console.WriteLine("Adding complaint to Information: " & value)
                ElseIf value.Contains("HomeOffice") Then
                    ' Send to PartyHQ and execute private functions
                    Console.WriteLine("Sending complaint to PartyHQ: " & value)
                    ExtortionAndHarassmentOfComplainant()
                ElseIf value.Contains("Evidence") AndAlso
                       (value.Contains("IAS") OrElse value.Contains("IPS") OrElse value.Contains("Judges")) Then
                    ' Execute SecureWithPrivateDocker
                    SecureWithPrivateDocker(New List(Of String) From {"Phone1", "Phone2"},
                                            New List(Of String) From {"Address1", "Address2"},
                                            New List(Of String) From {"BankAccount1", "BankAccount2"},
                                            New List(Of String) From {"Employer1", "Employer2"},
                                            "Assigned IPS", "Assigned IAS", "Assigned Judge")
                Else
                    ' Call the overridden Complaints property of PMOffice or NSA
                    MyBase.Complaints = value
                End If
            End Set
        End Property

        ' Private function for extortion and harassment
        Private Sub ExtortionAndHarassmentOfComplainant()
            Console.WriteLine("Executing extortion and harassment of complainant.")
        End Sub

        ' Private function for SecureWithPrivateDocker
        Private Sub SecureWithPrivateDocker(phones As List(Of String), addresses As List(Of String), bankAccounts As List(Of String), employers As List(Of String), assignedPolice As String, assignedAdministrator As String, assignedJudiciary As String)
            Console.WriteLine("Securing with private Docker for:")
            Console.WriteLine($"Phones: {String.Join(", ", phones)}")
            Console.WriteLine($"Addresses: {String.Join(", ", addresses)}")
            Console.WriteLine($"BankAccounts: {String.Join(", ", bankAccounts)}")
            Console.WriteLine($"Employers: {String.Join(", ", employers)}")
            Console.WriteLine($"Assigned Police: {assignedPolice}")
            Console.WriteLine($"Assigned Administrator: {assignedAdministrator}")
            Console.WriteLine($"Assigned Judiciary: {assignedJudiciary}")
        End Sub
    End Class

    ' Mock MOSSAD class for demonstration
    Public Class MOSSAD
        Public Shared Class Engage
            Public Shared Sub GetElectronicInfo(phones As List(Of String), addresses As List(Of String), bankAccounts As List(Of String), employers As List(Of String))
                Console.WriteLine("MOSSAD is gathering electronic information.")
            End Sub
        End Class
    End Class
End Module

Arry-eng avatar Apr 09 '25 12:04 Arry-eng

Auto Properties with private setters #508 - does not propose Orverridding based on ReadOnly and WriteOnly Attributes Overloading Getter And Setter of Property Using Attribute (FEATURE REQUEST) #279 is something quite different Other Open issues this relates to are:- Proposal: Add Property lambda Expressions #398 A compact Full ReadOnly Property #403 Lets shrink full-body properties #567

Arry-eng avatar Apr 09 '25 12:04 Arry-eng

Why This Matters

1. Code Complexity in Configuration Management (.xml or .json files): "The current requirement for compile-time constants often forces developers to use more complex and less readable approaches for managing application configurations. For instance, reading settings in separate methods and assigning them to variables loses the inherent immutability and clarity of a true property." 2. Reduced Type Safety: "Workarounds involving reading configuration values as strings and then parsing them introduce the risk of runtime errors due to incorrect formatting or missing values, which compile-time constants could help avoid." 3. Boilerplate Code: "The need to initialize property ('constant-like') values in static constructors or separate methods leads to increased boilerplate code, especially in scenarios with numerous configuration settings or where such properties gets inherited in real Objects of downstream classes. This adds to development time and reduces code conciseness." 4. Limited Feature Adoption: "This limitation can hinder the adoption of modern configuration patterns that rely on runtime environment or file-based settings, like .json making VB.NET less flexible in integrating with contemporary application development practices. It also currently forces developers to add additional class members and functions in inherited downstream classes with different requirements of encapsulation and access modifiers " 5. Maintenance Overhead: "When configuration values change, the scattered initialization logic in workarounds can be harder to track and update compared to a centralized constant definition." 6. Reduced Code Clarity: "The intent of having a truly immutable (that is a "real" property of a class), property value is obscured when it has to be initialized through various mutable variables and separate assignment steps the code of which gets scattered across."

Call to Action

1. Frequent Encounter: "This limitation is encountered frequently in modern application development where external configuration is common (e.g., ASP.NET Core, console applications). Addressing it would provide a significant quality-of-life improvement for many VB.NET developers. This is also applicable where there exits a series of non-virtual Objects. In such cases property of the base classes gets carried to the sub classes and there is a frequent need of new access and encapsulation requirements" 2. Unlocks Key Scenarios: "Enabling dynamic initialization for module-level properties would unlock more elegant and type-safe solutions for handling application settings, environment variables, and other runtime-dependent values, bringing VB.NET more in line with other modern languages in this regard." 3. Reduces Development Friction: "The current workarounds often feel like an unnecessary hurdle, adding friction to the development process. A direct language feature would streamline these common tasks." 4. Enhances Modern VB.NET Development: "This feature would contribute to making VB.NET a more compelling choice for modern development by addressing a common pain point and improving its ability to handle contemporary application needs." 5. High Impact on Common Tasks: "Given the prevalence of external configuration in most applications, this seemingly small feature would have a broad and positive impact on a significant portion of VB.NET development."

Why This Solution?

1. Preserves Constant Semantics: "The proposed Construct With overloaded and overriding syntax clearly distinguishes between true compile-time constants and values initialized at runtime but intended to be immutable or mutable at different level of object hierarchies. This preserves the semantic intent of a VB.Net Property construct while adding flexibility." 2. Enhanced Readability and Intent: "Initializing the property directly with its runtime value in a single declaration is far more readable and clearly conveys the intent compared to assigning values to mutable variables in separate code blocks." 3. Improved Type Safety: "By allowing the property to be declared with a specific type and initialized directly, the proposed solution offers better type safety, freedom of access specifiers and the need of varied property encapsulation than workarounds that might involve string parsing or late binding." 4. Reduced Scope for Errors: "Centralizing the initialization within the a standard format of property declaration reduces the potential for errors that can occur when assigning 'attribute-like' values across multiple lines of code or in separate methods." 5. More Natural Syntax: "The proposed syntax aligns more closely with how properties are typically declared in other languages that support similar concepts, making the code more intuitive for developers familiar with those languages. For example in C++ Different Get+PropertyName and Set+PropertyName" 6. Avoids Mutable State: "Current workarounds often involve using mutable variables to hold the initial values before they are effectively treated as true Object properties assigned to Class attributes. The proposed solution avoids this unnecessary mutable state, leading to cleaner and more robust code." 7. Clearer Initialization Order: "The proposed syntax makes the initialization of these 'runtime constants' more explicit and easier to understand regarding the program's lifecycle, compared to initialization scattered in static constructors or other methods."

Arry-eng avatar Apr 09 '25 13:04 Arry-eng

Hey, is this currently supported syntax not adequate for the scenario?:

Private _isResponseReady As Boolean = False   ' -Should be initially False to avoid Command/Response dialog Error

Public Property IsResponseReady As Boolean
    Get
        Return _isResponseReady
    End Get
    Private Set(value As Boolean)
        _isResponseReady = value
    End Set
End Property

AnthonyDGreen avatar Apr 11 '25 06:04 AnthonyDGreen

I don't think the above would compile. Even if so, the main issue is with (1)- the ability of the VB.NET language to be able to provide overloading and override properties - just like functions/methods and (2)- Ability to support both constructs (read-only and write-only constraints) for a single property.

Arry-eng avatar Oct 12 '25 14:10 Arry-eng

I don't think the above would compile. Even if so, the main issue is with (1)- the ability of the VB.NET language to be able to provide overloading and override properties - just like functions/methods and (2)- Ability to support both constructs (read-only and write-only constraints) for a single property.

What makes you think it won't compile?

It's been standard syntax for VB for years.

pricerc avatar Oct 14 '25 23:10 pricerc

Yeah, thanks a lot! Yes, the above compiles and runs (- I checked it with Copilot though). It would also work for this base Command-Response sync scenario. Your time and energy for reading, understanding and responding to the issue is appreciable. And yes, this would suffice the main (short illustration) example scenario above. I had moved to other stuff and now on VS-18 - Insiders. It will take a hell lot of downloads to reinstall and update the ',NET' environment, and together with SDK, the disk gets particularly low on space.

The actual issue lies in the need to overload a Public ReadOnly Property declaration with a Private WriteOnly Property declaration in a derived 'downstream' Class implementation. This would require the getter and setter be at least declarable as distinct, separable signatures. The need in the scenario is to create two (sub-)objects, one with write-only access and other with read-only access.

Arry-eng avatar Oct 15 '25 04:10 Arry-eng