I am creating a custom component and I need to create a property called "ReadOnly", and I get error message: "Not valid use keyword as an identifier"
Code is as follows:
'ReadOnly
Public Property ReadOnly As Boolean
Get
Return Me.Properties.ReadOnly
End Get
Set(ByVal value As Boolean)
Me.Properties.ReadOnly = value
End Set
End Property
What is wrong?
Thanks.
The problem is pretty obvious...You are using a reserved keyword as a name of a property, and you can't do that. It's almost like writing this code: Dim Integer As Integer. It can't be done.
You might wanna take a look here. As it says :
The following keywords are reserved, which means you cannot use them as names for your programming elements such as variables or procedures. You can bypass this restriction by enclosing the name in brackets ([ ]).
Related
Given the following VBA code, assuming that a Something is just a VBA class module....
Public Type Foo
SomeThing As Something
End Type
Public Sub TestFoo()
Dim x As Foo
With x
'Correct way to do it
Set .someThing = New Something
End With
With x
'This is wrong but realized only as a RTE
'438: Object doesn't support this property or method
.SomeThing = New Something
End With
End Sub
In contrast, if you change the type to something like VBA.Collection as below:
Public Type Foo
SomeThing As VBA.Collection
End Type
Public Sub TestFoo()
Dim x As Foo
With x
.SomeThing = New VBA.Collection
End With
End Sub
This is now a compile error, with Argument Not Optional. This is obviously wrong but why is it a compile-time error only with VBA.Collection?
This is explained in the VBA Language Specification. The semantics of the assignment inside the With block is driven by whether or not the statement is a Set statement or a Let statement. In this context, it is a Let statement:
With x
.SomeThing = New Something
End With
Note that in the VBA grammar, the keyword Let is optional (obsolete):
let-statement = ["Let"] l-expression "=" expression
In a Set statement, the Set keyword is required:
set-statement = "Set" l-expression "=" expression
Inside of a With block, the l-expression is basically the UDT member, although the exact same behavior applies if x is used directly.
When evaluating a Let expression, the semantics are described in section 5.4.3.8:
Static Semantics.
This statement is invalid if any of the following is true:
<expression> cannot be evaluated to a simple data value (section 5.6.2.2 ).
Following that to 5.6.2.2 (Evaluation to a simple data value), the following runtime semantics apply (applicable rule only):
Runtime semantics.
At runtime, the simple data value’s value and value type are
determined based on the classification of the expression, as follows:
If the expression’s value type is a specific class:
If the source object has a public default Property Get or a public
default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Otherwise, if the source object does not have a public default
Property Get or a public default function, runtime error 438 (Object
doesn’t support this property or method) is raised.
Thus the runtime error 438 for SomeThing As Something.
With the Collection, the Let static semantics still apply, but it fails the static semantics of 5.6.2.2 (which gives the compile error). Again, the preceding inapplicable semantics are omitted:
Static semantics. The following types of expressions can be evaluated to produce a simple data value:
An expression classified as a value expression may be evaluated as a simple data value based on the following rules:
If the declared type of the expression is a specific class:
If this class has a public default Property Get or function and this default member’s parameter list is compatible with an argument
list containing 0 parameters, simple data value evaluation restarts as
if this default member was the expression.
The default member of Collection is a function (.Item) that takes a single parameter Index. In this code, the parameter is not provided, so the argument list is incompatible:
With x
.SomeThing = New VBA.Collection
End With
Thus the Argument Not Optional compile error.
In typing up the question, it struck me that the VBA.Collection had a default member. Thus, the compiler was interpreting the .Something = New VBA.Collection as an assignment to the default member... except that the Item is an indexed property. That explains why we get Argument not optional which would be quite a strange error to get with a Set statement.
In contrast, a VBA class module might not have a default member at all, so that there is no indexed property + default member to trigger a compile-time error. However, it also means that the bad syntax won't be caught until runtime.
I was reviewing some of my colleagues vb.net code the other day and was mystified at a new level - Unfortunately I don't have the code at hand but it looked something like this:
Public Class foo
Public Function [new]([bar] As String, [baz] As String) As String
Return String.Concat([bar], baz)
End Function
End Class
I have never seen these sharp parenthesis surrounding the name of the function and variable. Anyone can explain to me what the purpose of this is.
It's because "new" is a keyword and using [ ] is telling the compiler that it should read the keyword as a literal string instead. This way you can use keywords as variable and method names ... if you wanted.
I think the usage around the variables [bar] and [baz] is just .... well, because he could.
Public Sub New()
If GridElement.typeList(getId(), getLayer()) Is Nothing Then
GridElement.typeList(getid(),getlayer()) = GetType(me)
End If
End Sub
Take a look at the snippet above. On the third line, I'm trying to set a multidimensional array to the type that the current class is. How can I get the type of the class that I am currently in?
I'm using VB.net so all .net examples are acceptable.
Just call GetType() (i.e. effectively me.GetType()) - you're already in an instance.
Actually, GetType() returned a "Type Expected" error.
I ended up using [GetType]() instead.
Since I wanted to return the type name in an error message, the complete result was something like this:
Throw New Exception($"Error in the {[GetType]().ToString} Class.")
I have a class like this:
Public Class MyClass
Private _intList As New List(Of Integer)
Private _avg As Decimal
Public Sub Add(ByVal anInt As Integer)
_intList.Add(anInt)
End Sub
Public Property Avg() As Decimal
Get
Dim _sum As Integer = 0
For Each anInt In _intList
_sum += anInt
Next
Avg = If((_intList.Count > 0), _sum / _intList.Count, _avg)
Return _avg
End Get
Set(ByVal value As Decimal)
If _avg <> value Then
_avg = value
Console.WriteLine("Value changed")
End If
End Set
End Property
End Class
The Getter is calculating average and calls Setter to save the value. For some reason I cannot understand, the average is always 0. For example:
Dim c As New Class2()
c.Add(1)
c.Add(2)
c.Add(3)
Console.WriteLine(c.Avg.ToString()) ' This will print 0
Did I do something wrong? What is the cause of this?
Wow, I think you've discovered a very strange behavior of VB: when you are inside the definition of a function, you can return a value either with Return or by using = to "set" the function's value.
Like this:
Function GetInteger() As Integer
GetInteger = 5
End Function
In the above function, the line GetInteger = 5 is basically equivalent to Return 5*.
OK, so you probably already knew that. But here's the weird part, and I had no idea this was the case until testing it just now (admittedly, on Mono, but I am seeing the same behavior you are): apparently this applies to property getters as well. So look at this line:
Avg = If((_intList.Count > 0), _sum / _intList.Count, _avg)
You're actually not calling the property setter there; you're setting the return value for the getter. You can verify this by removing the line Return _avg; suddenly you will see your getter starts returning the actual average.
*Not exactly the same, as you could later set GetInteger to something else without returning immediately whereas using Return ensures the function returns right away.
This is by design and explicitly mentioned in the Visual Basic Language Specification, chapter 9.7.1:
A special local variable, which is implicitly declared in the Get
accessor body's declaration space with the same name as the property,
represents the return value of the property. The local variable has
special name resolution semantics when used in expressions. If the
local variable is used in a context that expects an expression that is
classified as a method group, such as an invocation expression, then
the name resolves to the function rather than to the local variable.
For example:
ReadOnly Property F(i As Integer) As Integer
Get
If i = 0 Then
F = 1 ' Sets the return value.
Else
F = F(i - 1) ' Recursive call.
End If
End Get
End Property
Solve your issue by assigning the _avg field directly. Property getters with side-effects like this is best avoided.
Your setters and getters really should just be returning properties, and not doing the calculations themselves. Try creating a method like calcAvg() that does the average calculations, and on the Add() call that method (which should internally not re-perform the whole average calculation, but simply update it. Let me know if you're not sure how to do that). That calcAvg() method will set the _avg instance variable.
Also, I'm not sure it really makes send to have a setter for the average. An average of numbers is a derived property, not something that should be set by an external user.
MrDanA answer is more correct than what I'm going to give you, however, I believe the reason why you are getting the value of 0 is because you are never setting the variable _avg to anything. After you do your AVG calculation if you do:
_avg = AVG
return _avg
I get a value of 2 when I do this.
Like I said before though... MrDanA's answer is a better way to go.
I used developerfusion.com to convert a snippet of my C# code to VB .NET and I noticed the String type translated into [String].
I tried Google and Searching SO to no avail so I will ask the community is there a difference between [String] and String? And if so what is/are the difference(s)?
In VB.Net, the [] surrounding a word is used to allow a keyword to be used as a normal identifier. So using [String] means I want to identify something with the word String and not the VB keyword String.
The converter probably did that because it didn't recognise the string type and thought that it was a class defined in your code.
You can put brackets around identifiers to use keywords. You could create your own [String] class that would be different from the built in String class, but that could of course easily get confusing...
Public Class [String]
Public Value As Integer
End Class
Dim s As New [String]
s.Value = 42
Brackets usually indicate a variable name that using a reserved keyword. Did you name string variables String?
Brackets can be used in VB.NET to allow VB.NET keywords to be used as user defined names. For example, you could create a class called Integer, even though there is already an Integer keyword in VB.NET:
Public Class [Integer]
End Class
Hope this clarifies!