How do I use Thread.VolatileWrite with reference types with Option Strict On? - vb.net

Wrapping the argument in CObj or DirectCast shuts the compiler up, but the value is still not written.
Option Strict On
Imports System.Threading
Module Module1
Dim str As String
Sub Main()
Thread.VolatileWrite(str, "HELLO") ' Compiler error.
Thread.VolatileWrite(CObj(str), "HELLO") ' Fails silently.
Thread.VolatileWrite(DirectCast(str), "HELLO") ' Fails silently.
Console.WriteLine(str)
End Sub
End Module

There is no overload of Thread.VolatileWrite which takes a String argument. The only reference type supported is Object.
Because VolatileWrite is updating the variable str and Option Strict is On the compiler complains because in theory VolatileWrite could attempt to write a value to that variable which is not of type String (the compiler only sees that it might write any Object). In fact, as the VolatileWrite method also only takes a String you could write code which would attempt to do this. It would fail for reasons beyond the scope of this question.
When you wrap the expression in a COjb/CType/DirectCast expression (really anything with parenthesis) then the variable is no longer considered a variable but a value - it's treated the same way as if you'd just type a string literal there. Since values don't have storage locations the ByRefness of VolatileWrite is ignored which means it no longer writes which means it can no longer write a bad value which means the compiler doesn't need to warn anymore.
To get the behavior you want with a string type variable use the System.Threading.Thread.MemoryBarrier method before your writes and after your reads. See this thread for additional information: How do I specify the equivalent of volatile in VB.net?

Related

Passing an rvalue as a ByRef parameter to VB6?

Background: I've got a set of VB6 DLLs that share a common "interface". Whichever version is installed locally has members of this interface invoked via COM interop (from VB.Net code, which I suspect might matter). I noticed today that one of the invocations passes [what I understand to be] an rvalue (hereinafter "an rvalue") to a VB6 function that does not have that particular parameter defined as ByVal.
Example Code:
VB6:
Public Function VB6Function(input As String) As String
' Do interesting things with input
End Function
VB.Net:
' get an instance of the VB6 class and pass our trimmed localString to it
result = vb6Instance.VB6Function(localString.Trim())
' Do interesting things with localString
I have not yet noticed an instance of the VB6 code changing the value of input, but I also haven't done an exhaustive search of the different DLL implementations (there are several hundred).
What would happen if VB6Function did change the value of input when input is "an rvalue"? For that matter, why doesn't this method invocation simply error out when "an rvalue" is passed?
What would happen if VB6Function did change the value of input when input is "an rvalue"?
Nothing. Or rather, nothing interesting.
When the called function changes the value of its argument, it makes no difference for the insides of that function whether the argument was provided byval or byref. All that matters is that there is a variable of certain type, thus, it can be acted upon.
For that matter, why doesn't this method invocation simply error out when "an rvalue" is passed?
Why would it error out? The passed argument as correct type (string), that is all that matters.
There is no notion of an rvalue in VB.
When you pass what you would call an rvalue to a method accepting something by reference, the compiler automatically passes the reference to a temporary location where the rvalue actually resides. The method gets its value byref, the caller does not care about pointers.
localString.Trim() allocates and returns a string. It has an address and can be passed around. Your code does not explicitly capture that address, but the compiler has no problem passing it to VB6Function byref. If VB6Function changes the value, it changes what that temporary location points to, which has no observable difference because it's going to be destroyed after the call either way.
As for why some people may have preferred receiving strings byref in VBA, it's specifically to avoid copying the entire string each time when calling the function. In VB.NET it's not a problem because strings there are immutable and therefore can be passed byval without copying, but in VBA that is not the case, so a byval string needs to be cloned for the purpose of the call. People avoided that by specifying byref, even though that technically gave them the power to mess with the passed variable.

Format - Expected Array

I keep getting an error when I try to format this number. I've done it in VBA before and I tried to change the SOPID to a variant, a string, and an integer.
Dim SOPID As Integer
SOPID = DMax("file_id", "tblSOP") + 1
'Output test data
MsgBox (format(SOPID, "000"))
I have no idea what I am doing wrong.
Assuming the code was pasted directly from your IDE, the casing of format is suspicious; that would be Format, unless there's a format variable or function that's in-scope, ...and that's being invoked here.
Look for a public (could be implicitly Public, or if it's in the same module then it could also be Private) format function procedure that expects an array argument: that's very likely what's being invoked here.
Rubberduck (free, open-source; I manage this project) can help you easily identify exactly what's being invoked and an inspection would tell you about any shadowed declarations, but to be sure you can fully-qualify the function call to avoid inadvertently invoking another function that's in scope:
MsgBox VBA.Strings.Format$(SOPID, "000")
Note that there are no parentheses around the argument list of a parameterized procedure call in VBA; the parentheses you have there are surrounding the first argument and making the expression be evaluated as a value that is then passed to the invoked function: this isn't necessary.
Also note the $: the VBA.Strings.Format function takes and returns a Variant; VBA.Strings.Format$ takes and returns a String. If you aren't dealing with any Null values (an Integer couldn't be Null), consider using the String-returning alias.

Using statement, As vs =

Is there a difference between using 'As' keyword and the '=' operator in vb.net?
Example:
Using aThing As New Thing()
...
End Using
' OR
Using aThing = New Thing()
...
End Using
There will be no effective difference if you have Option Infer On. If you have Option Infer Off then the first snippet will always result in a variable of type Thing while the second snippet will fail to compile with Option Strict On and result in a variable of type Object with Option Strict Off.
The first code snippet is explicit in its typing of the variable so it will be the type you specify regardless of what settings you have for Option Strict and Option Infer. The second code snippet is not explicit about the type so that type must be determined implicitly by the compiler. With Option Infer On, the type Thing can be inferred from the initialising statement. With Option Infer Off, the type will default to Object and late-binding must be used, which is not allowed with Option Strict On.
It's worth noting that your original question isn't really valid because it's actually not a case of using As or =. This:
Using aThing As New Thing()
is actually just a shorthand for this:
Using aThing As Thing = New Thing()
so you're actually using = either way and the choice is just whether or not to provide an As clause. An As clause is required with Option Strict On unless you also have Option Infer On and the type can be inferred from the initialising statement. If there is no initialising statement or the type of that statement is different to the type you want the variable to be then an As clause is required to tell the compiler the type of the variable that it cannot infer for itself.

Why doesn't VB.Net type inference work in class fields?

If I were to type the following into a method body:
Dim myInt = 1
the Visual Studio IDE (and therefore, I am guessing, the compiler) infers the type of myInt to be Integer.
EDIT
Apparently using a literal was a bad choice here, since I've become embroiled in a lengthy debate that has nothing to do with the question. If you take issue with the fact that the expression 1 might be interpreted as an instance of different numeric types, pretend I had written:
Dim myInstance = New MyClass()
END EDIT
However, when I put a field declaration with the exact same code at the top of a class, the type of myList is not inferred:
Public Class Foo
Dim myInt = 1
End Class
On mouseover, it mentions the absence of an As clause, and says a type of Object has been assumed. I cannot pass myInt as an argument to a function or sub that expects an Integer argument, without explicitly adding an As clause or casting to Integer.
Is there a discrepancy between how the IDE and compiler deal with type inference? If, on the other hand, the compiler can't infer type in this situation either, why the discrepancy between method variables and class fields?
What you've found is that way on purpose. here is the MSDN expalanation.
Local type inference applies at procedure level. It cannot be used to
declare variables at module level (within a class, structure, module,
or interface but not within a procedure or block). If num2 in the
previous example were a field of a class instead of a local variable
in a procedure, the declaration would cause an error with Option
Strict on, and would classify num2 as an Object with Option Strict
off. Similarly, local type inference does not apply to procedure level
variables declared as Static.

Enforce Type Alias in VB.NET

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.