When Option Strict is turned Off, which is the default for my application, VB will automatically convert the value when property is String. It appears to call the toString() of the incoming value's Object. I believe casting will work for any property that can be safely casted (eg integer to double). I want the properties to behave in a strict manner, in that the Type passed MUST match the type on the declared property.
Is there a way to make Objects Properties Strict at the Object level without having to resort to the Option Strict On configuration on?
For example:
Option Strict Off
Class TestObj
Private _foo As String
Public Property Foo as String
Get
Return _foo
End Get
Set(v As String)
console.write(String.format("v = <{0}> {1}", v.GetType().FullName, v))
_foo = v
End Set
End Property
End Class
Dim o as new TestObj()
o.Foo = "some_str"
o.Foo = 1234
o.Foo = DateTime.now()
In the above case, all values are converted to Strings at arrival to the setter:
v = <System.String> some_str
v = <System.String> 1234
v = <System.String> 7/7/2022 8:22:49 PM
I am aware of DynamicObject and ability to tightly control setters like Python's __setattr__ but this breaks autocomplete functionality in editors as it would preclude defining properties on the class to get the functionality.
I strongly agree with the commenters that Option Explicit On and Option Strict On are simply a must - as the default settings of Visual Studio for new VB projects as well as for each project within the solution(s) you took over.
I my opinion one has to invest first some effort into clean-up work when taking over legacy solutions including
Add an ".editorconfig" if there isn't any
Convert the projects to SDK projects where possible
Go through the code and
format the code to your liking using the current naming convention where possible
correct/complement/remove/translate-to-English the documentation comments
add some //TODO: - comments where refactoring is needed wherever you notice a code repetition, repeated property access, huge methods that needs to be split into smaller parts, if-not-condition-then-else that needs to be reverted etc. but don't code it yet - these refactorings could introduce some bugs, we need to finish the next step first.
put every top-level type in its own file (except overloaded generic types of the same name) and make sure the type name matches the file name
Go through the unit tests and fix them, remove unnecessary ones and complement the missing ones.
Change all projects to Option Explicit On, Option Strict On and Option Compare Binary (use explicit StringComparer.OrdinalIgnoreCase / StringComparer.InvariantCultureIgnoreCase where needed). (Option Infer On or Off, what you prefer). If you hate to have 1000s of errors, add an Option Strict Off on top of each VB file which you later removed again file by file, otherwise do a bulk mutation.
Fix all compile errors (per file or bulk mutation)
Run unit tests, fix code of failing tests (repeat this after each of the following steps)
Update the .NET framework version if possible (e.g. to current or .NET 4.8)
Update the NuGet packages
Do all the previously tagged refactorings
Enable the analyzer warnings
Fix all the warnings and infos or suppress them, individually or in the project file by adding the according entry to the <NoWarn> element.
Start implementing the changes why you originally cloned the repository...
Of course that comes with a price but not doing it also has its price - I'm certain you are going to find dozens of little mistakes in the original code during the process of going Strict. And the time one spends formatting the code files is not just wasted time neither as in the process one also learns to understand the code...
Related
Consider the following line of code which used to compile without warnings.
Public SetUpDone = False
After upgrading a project to Visual Studio 2017 from Visual Studio 2005 over a hundred of these BC42020 warnings exist. The MSDN description of the warning simply states that the variable defaults to type object. I don't have a good idea of the seriousness of this type of warning. The debugger indicates that the code executes as I expect. Is it merely a performance type of issue?
Secondly, I thought that Visual Basic supported some form of Type Inference so I'm not clear about why it wouldn't be able to deduce that the type should be Bool.
Another example is the following where the function returns a String
Dim dayTxt = " " & GetTextFromIni("General", "Var50")
I would have thought that type inference would work here and deduce that dayTxt is a String.
This:
Public SetUpDone = False
Is equivalent to this:
Public SetUpDone As Object = False
As suggested, type inference is only for local variables, not fields. With Option Infer On, this inside a method:
Dim SetUpDone = False
would indeed be equivalent to this:
Dim SetUpDone As Boolean = False
There are a couple of issues with the code as you have it. Firstly, it means that every use of that False value requires unboxing which makes your code slower. That's the case for any value types, i.e. structures. Value types are normally stored on the stack but, when boxed, are stored on the heap.
Secondly, it means that any member access will require late binding. That's not an issue for Boolean values because they have no members of interest anyway but if it was, say, a DateTime then the IDE would never provide Intellisense for that type because al it would see would be type Object and the existence of the specified member would have to be confirmed at run time, making the code less efficient again.
Thirdly, it means that the compiler can never confirm that you're passing the correct type as a method argument. For instance, if you have a method with a Boolean parameter, the compiler won't know that you're passing a Boolean if you pass that field because it's type Object. That also means that if you pass some other Object variable that doesn't contain a Boolean, the compiler can't warn you.
As suggested, you should turn Option Strict On in the project properties. That will flag every instance of you're not specifying the appropriate type for something. Fixing those errors will, at the very least, make your code a bit more efficient. It may even draw your attention to situations where exceptions will or could be thrown at run time. Having Option Strict On enforces strict typing so it makes you think more about the types you're using. Even if you're conscientious about that with Option Strict Off, you can still make mistakes that Option Strict On will prevent.
I'm turning On Option Strict on all project in my newly inherited VB.NET application. I'm mostly adding alot of CStr, CBool, CType statements to get rid of all the compile errors.
Dim x As String = someObject
dim val As SomeEnumType = 1
becomes
Dim x As String = CStr(someObject) ' Not .ToString() because someObject could be Nothing
Dim val As SomeEnumType = CType(1, SomeEnumType)
etc.
I'm doing everything pretty much by hand one error at a time and have a test application to test the Nothing, ... bordercases.
But is it possible I'm missing something that is going to generate exceptions at runtime?
And what kind of code is being generated due to Option Strict? Is it just some conversions that are going to be added or does OptionStrict do other things aswell?
Option Strict On does not generate any extra code, it merely tells the compiler to generate errors when your vb.net statements are relying on implicit type conversions. Like assigning an object to a string. What you've written in your snippet is exactly what the compiler does with Option Strict Off so no extra code is generated by your type conversion operators.
But of course, there's always a non-zero chance that you use the wrong conversion and break the existing code. You'll have to do what's always required when you make changes to code, you'll have to re-test it.
I'm writing some code in Visual Basic 6 and I have noticed that I don't even need to declare variables for things to work.
The following (explicit declaration):
Dim foo As String
foo = "Bar"
Seems to work just as well as this (implicit declaration):
Dim foo
foo = "Bar"
Or this (no declaration):
foo = "Bar"
I know in C# I need to declare a variable before I use it, and that implicit and explicit declarations are both acceptable. I also know that in Python, you don't declare your variables at all before you use them.
In regards to Visual Basic 6 (and by extension VBA) which is proper?
Thanks
It's a good HABIT.
There is a VB option called Option Explicit. With that set to ON, then VB forces you to declare a variable before you use it: no more
foo = "Bar"
That helps with mistyping the variable name later in your code... without that, you can typso the variable name, your program compiles but won't work, and it's HARD to dig that out.
In Tools/Options, Editor tab, check the Require Variable Declaration checkbox. This will automatically add Option Explicit to every new code module.
I would say this is more than a best practice; I think of it as a requirement for programmer sanity. The setting is persistent; once set, it stays enabled. Microsoft made it an option because some older versions of VB didn't have the feature, which also explains why it was disabled by default.
Should I explicitly declare my variables in VB6?
Yes. Why?
Not just because it is a good habit or it is a must but because of only one main reason which I have mentioned in this post as well.
VB defaults the variable to being type Variant. A Variant type
variable can hold any kind of data from strings, to integers, to long
integers, to dates, to currency etc. By default “Variants” are the
“slowest” type of variables.
AND
As I mentioned earlier, If you do not specify the type of the
variable, VB defaults the variable to being type Variant. And you
wouldn’t want that as it would slow down your code as the VB Compiler
takes time to decide on what kind of variable you are using. Variants
should also be avoided as they are responsible for causing possible
“Type Mismatch Errors”.
Topic: To ‘Err’ is Human (See Point 3)
Link: http://siddharthrout.wordpress.com/2011/08/01/to-err-is-human/
The above link also covers other parts related to coding that one can/should take care of.
HTH
I highly reccomend that you always declare your variables. This can be forced by setting Option Explicit in each code module. You can let VB6 do that automatically for you by going to Tools->Options, in the Editor tab check Require variable declaration.
If you don't use Option Explicit, then a variable will be automatically created for you each time you reference a previously unknown variable name. This is a very dangerous behavior, because if you mistype a variable name, an empty variable will be created for you, causing unexpected behavior of your code.
You don't have to declare the type of your variables but I would also recommend that you do that. The default type of a variable is Variant, which has a small performance overhead and create some problems if you are creating COM objects for use by C++ or C# (if anybody does that anymore).
How might one go about aliasing a type in VB.NET or C# such that when the alias is used as an argument type in a function, accidentally using the not aliased type is an error?
i.e.
Imports AccessSpecifier = System.String
Module Accessors
Delegate Function IoOper(ByRef strm As System.IO.Stream) As Action
Public Function accessMethod(ByRef spec As AccessSpecifier) As IoOper
' implementation
' ...
End Function
End Module
Module Main
Public Sub Main()
Dim spec As AccessSpecifier = New AccessSpecifier(CType("READ_WRITE", Char()))
Dim val = Accessors.accessMethod(spec)
System.Console.WriteLine(val.GetType())
Dim shouldFail = Accessors.accessMethod("FAIL_ME")
System.Console.WriteLine(shouldFail.GetType())
End Sub
End Module
Or perhaps is there a better way to go about this?
Overall, I'm wanting the IDE to force me to know what I'm doing if I'm throwing Ints around to mean Flags, or States and Strings around to mean Names, Propertys and Records.
Help?
I've never liked Type aliasing in .NET. It makes for imprecise code and it is not immediately clear what is happening. As in your example, when an individual went looking for the AccessSpecifier.vb file (or class) they would not find it.
Also, Aliasing only works within YOUR project and only within a single code file. So you would have to define that alias in all the various code files where it was to be used.
A better (as in easier to read, easier to maintain, and more clear of intent) option is to create a class and overload the type conversion operators for automatic conversion to/from String. In this manner you retain your ability to use your new class as if it were a String, but you gain your strict type checking.
What is the best way to manage common user-defined types across VBA modules?
I use the same user-defined types in different modules. For example, I often need to represent (x,y) points, so I end up having this Type in different modules:
Type XYpointType
x As Double
y As Double
End Type
I pass arguments of type XYpointType to and from subs and functions in different modules.
However, I suspect this is a poor way to manage user-defined types. The exact same Type definition code ends up in many different modules.
Alternatively, I could have this Type declaration in a single, central "types" module, and all other modules needing this particular type should refer to the types module. The downside is that each module loses its "modularity" in that it must be accompanied byt the "types" module wherever it goes.
Any suggestions?
This is interesting, because I never knew that you could have two modules both declaring a Public Type with the same name. Now that I do know, I'm horrified.
So first, Eric Towers is correct that you want to put your Type declaration in only one module. Other modules that use this Type will depend on that module being available, but that's just something that comes with modularization. The need to manage dependencies between modules is common to all software development. It's unfortunate that within a single VBA project (like the kind you'd find in a single Excel workbook) the only option for managing module dependencies is manual. (In VBA, "modules" are really "source code files". Other languages/environments have various higher-level packaging systems.)
Now for the horrifying part. If you do declare Types with the same name in different modules, you're setting yourself up for problems. Consider two VBA Modules:
'Module1
Public Type typ
x As String
End Type
Public Sub useTyp()
Dim t As typ
t.x = 42
Debug.Print t.x + t.x
End Sub
and
'Module2
Public Type typ
x As Long
End Type
Public Sub useTyp()
Dim t As typ
t.x = 42
Debug.Print t.x + t.x
End Sub
in the same project, your code will "compile" (at least in Excel VBA). But the two 'useTyp' subs give different output. Worse, if you remove one of the Type declarations from one of the modules, you've suddenly changed the behavior of the 'useTyp' routine in the same module! This kind of ambiguity is never desirable.
What's going on is that VBA is really creating two different types, 'Module1.typ', and 'Module2.typ'. When you're in the same module and don't qualify the type name, VBA silently finds the "right" type and uses it.
I'm a little surprised to hear that you're passing around instances of one 'XYpointType' to modules that have a different 'XYpointType' declaration. It's not that important, but could you post the code? I'm interested in nitpicky things like this...
Use your second method. The point is to make a reusable Types blob. To be reusable, it must be separated. Then, yes, every module that uses those types must reference that blob. But the same thing may be said about modules calling into each other, forms requiring the modules they call, et c.
You can also create a class for your XYPoints. This will allow you to have custom functions, and methods should you need to go down that road. Types are very limited compared to classes.
Here is a good resource to get you started: http://www.cpearson.com/excel/Classes.aspx