VBCodeProvider Can not cast correctly the Implicit Variable declaration on compile - vb.net

I am Compiling My string Code(I read My Code from Text File) In vb and it works fine but i have a function that returns nullable double(Double?)
when i use it like this
Dim x As Double? = Myfunc(1000) 'it returns Nothing
my x variable fills with Nothing and it's ok
But When I use it like this
Dim x = Myfunc(1000) 'it returns Nothing
my x value is 0 !!!!
How can i solve this problem
i want my users write codes like first code block
i tested all Option Explicit and Option Strict but it did not gain me anything.
please let me know how can i use Just dim x not Dim x as (type)
thank you for your helps
UPDATE :this is Myfunc Code :
Function Myfunc(parameterId As Long) As Double?
If parameterId = 1000 Then
Return Nothing
Else
Return tot(parameterId) 'it is a dictionary of values
End If
End Function
And this Is my Compile Class :
Private Shared Function Compile(ByVal vbCode As String) As CompilerResults
Dim providerOptions = New Dictionary(Of String, String)
providerOptions.Add("CompilerVersion", "v4.0")
' Create the VB.NET compiler.
Dim vbProv = New VBCodeProvider(providerOptions)
' Create parameters to pass to the compiler.
Dim vbParams = New CompilerParameters()
' Add referenced assemblies.
vbParams.ReferencedAssemblies.Add("mscorlib.dll")
vbParams.ReferencedAssemblies.Add("System.Core.dll")
vbParams.ReferencedAssemblies.Add("System.dll")
vbParams.ReferencedAssemblies.Add("System.Windows.Forms.dll")
vbParams.ReferencedAssemblies.Add("System.Data.dll")
vbParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")
vbParams.ReferencedAssemblies.Add("System.Xml.dll")
vbParams.ReferencedAssemblies.Add("System.Xml.Linq.dll")
vbParams.GenerateExecutable = False
' Ensure we generate an assembly in memory and not as a physical file.
vbParams.GenerateInMemory = True
' Compile the code and get the compiler results (contains errors, etc.)
Return vbProv.CompileAssemblyFromSource(vbParams, vbCode)
End Function

As discussed above, Option Infer On needs to be included to force the compiler to create the variable as the required type - in this case the Double? returned by MyFunc.

Related

Dealing with error message narrowing from type object to type string

I am getting this error message when converting code from .Net 2.0 to .Net 4.5:
Option strict on disallows narrowing from type 'object' to type
'string' in copying the value of 'ByRef' parameter 'ParamValue' back
to the matching argument.
The code looks like this:
Public Shared Function TheFunction(ByRef x As Object ) As Integer
TheFunction = 5
// ultimately called like this: SqlCommand.Parameters.AddWithValue("field", x)
End Function
Private Function AFunction(ByVal x As String) As Boolean
Dim cnt As Integer = TheFunction(x)
End Function
I have googled for answers and it seems the suggestion is to change the TheFunction.
I am constrained in that I cannot change TheFunction.
I can turn off strict, but I would rather put in a good fix for this problem like copying x to a different variable and passing that variable in.
Would this work?
Dim boxedObject as Object = CType(x, Object)
Dim cnt As Integer = TheFunction(boxedObject)
x = CType(boxedObject, String)

Public member 'ToCSVValue' on type 'Integer' not found for VB Extension method

I am trying to write a ToCSV() extension in VB based on Scott Hanselman's blog. It could be that my C# to VB is not correct, but it all seems right.
I added a module with:
<System.Runtime.CompilerServices.Extension>
Public Function ToCSV(Of T)(items As IEnumerable(Of T)) As String
Try
Dim csvBuilder = New StringBuilder()
Dim properties = GetType(T).GetProperties()
For Each item As T In items
'' Test Code
Dim newline As String = ""
For Each l2 As Reflection.PropertyInfo In properties
' This works
newline &= l2.GetValue(item, Nothing)
' This works too
Dim int As Integer = 1234
Dim s As String = int.ToCSVValue()
'This works
Dim nl = l2.GetValue(item, Nothing)
' This blows up with "Public member 'ToCSVValue' on type 'Integer' not found."
' The Debugger type shows "Object {Integer}" which I assume to mean that the debugger interprets the object as an integer.
nl = nl.ToCSVValue()
Next
' Original code
Dim line As String = String.Join(",", properties.Select(Function(p) p.GetValue(item, Nothing).ToCSVValue()).ToArray())
csvBuilder.AppendLine(line)
Next
Return csvBuilder.ToString()
Catch ex As Exception
Throw
End Try
End Function
<System.Runtime.CompilerServices.Extension>
Private Function ToCSVValue(Of T)(item As T) As String
If item Is Nothing Then
Return """"""
End If
If TypeOf item Is String Then
Return String.Format("""{0}""", item.ToString().Replace("""", "\"""))
End If
Dim dummy As Double
If Double.TryParse(item.ToString(), dummy) Then
Return String.Format("{0}", item)
End If
Return String.Format("""{0}""", item)
End Function
When I call it with something like:
Dim s As String = ctx.Customers.Where(Function(x) x.CustomerID = 123456).Select(Function(x) New With {.CustomerID = x.CustomerID, .CustomerName = x.CustomerName}).ToCSV()
it gets to the function ToCSV just fine. It recognizes the items passed in. It pulls out the first item and sees that there are the 2 fields in it. All good!
The GetValue() works just fine.
If I create a static integer and call ToCSVValue on it, it works fine.
If I create a static string and call ToCSVValue on it, it works fine.
When I call ToCSVValue on the GetValue() I get:
Public member 'ToCSVValue' on type 'Integer' not found.
Likewise, if I have just strings in the dataset, I get:
Public member 'ToCSVValue' on type 'String' not found.
Ideally this would work as it is in the "Original code" section and I can kill all this other test code.
Can anyone tell me what is happening and why the "(Of T)" is not working the get GetValue() types, but it is for the directly cast types?
You need to have 'Option Infer On'.
When I use Option Infer On, it works fine.
If you don't use this, then VB is using 'Object' whenever you leave off the type.
Also, although this isn't causing your problem, the proper conversion of the ToCSV method is:
Public Function ToCSV(Of T As Class)(items As IEnumerable(Of T)) As String
The short answer is that calling it as a method ToCSVValue(p.GetValue(item, Nothing)) will work as in the C# version.
The longer answer is that you can't call extension methods on Object in VB. In VB Object is treated more like dynamic in C#. For example:
<Extension()> Function toStr(Of T)(item As T) As String
Return item.ToString
End Function
then this will result in compile-time Warning "Late bound resolution; runtime errors could occur." and a run-time Error "Public member 'toStr' on type 'Integer' not found.", but it will work in C#:
Dim i As Object = 123
Dim s = i.toStr

How do I copy Array values to a structure

I would like to to copy that values of an array into a Structure.
Example:
' The Array
Dim Columns(2) As String
' The Structure
Private Structure Fields
Public FName As String
Public LName As String
Public Email As String
End Structure
' I would like to map it like so:
Fields.FName = Columns(0)
Fields.LName = Columns(1)
Fields.Email = Columns(2)
Obviously I could write a function if it was so simple, but really there are over 25 columns and it's a pain to write a function that would map it.
Is there some way to do this?
There really is no simple way that will work in all cases. What you are complaining is too much effort is the only way to guarantee that it will work in all cases.
That said, if you can guarantee that the number of elements in the array matches the number of properties/fields in the structure/class and that they are in the same order and of the same types then you could use Reflection in a loop, e.g.
Private Function Map(source As Object()) As SomeType
Dim result As New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return result
End Function
EDIT:
The code I have provided works as is if SomeType is a class but, as I missed the first time around, not for a structure. The reason is that structures are value types and therefore a copy of the original object is being sent to SetValue, so the field value never gets set on that original object. In theory, to prevent a copy being created, you should be able to simply box the value, i.e. wrap it in an Object reference:
Private Function Map(source As Object()) As SomeType
Dim result As Object = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function
As it turns out though, the VB compiler treats that a little differently than the C# compiler treats the equivalent C# code and it still doesn't work. That's because, in VB, the boxed value gets unboxed before being passed to the method, so a copy is still created. In order to make it work in VB, you need to use a ValueType reference instead of Object:
Private Function Map(source As Object()) As SomeType
Dim result As ValueType = New SomeType
Dim resultType = result.GetType()
Dim fields = resultType.GetFields()
For i = 0 To source.GetUpperBound(0)
fields(i).SetValue(result, source(i))
Next
Return DirectCast(result, SomeType)
End Function

Invoking COM Properties and Methods

I am trying to dynamically create COM object, call COM method and set COM properties. The COM class is a VB6 ActiveX DLL. The implementation is exactly equal to the VB6 code from this page http://msdn.microsoft.com/en-us/library/ms973800.aspx.
In short words, the project is PhysServer and the class name is Temperature which has two properties Celsius and Fahrenheit and two methods GetCelsius() and GetFahrenheit().
I have already run regsvr32 to register the ActiveX DLL to the computer. The ProgID is PhysServer.Temperature.
I have three block of codes
Code Block 1 (works)
Option Explicit Off
Option Strict Off
...
Dim objType = Type.GetTypeFromProgID("PhysServer.Temperature")
Dim comObj = Activator.CreateInstance(objType)
comObj.Celsius = 100
Dim f As Double = comObj.GetFahrenheit()
Console.WriteLine(f) ' shows 212
Code Block 2 (works)
Option Explicit On
Option Strict On
...
Dim objType = Type.GetTypeFromProgID("PhysServer.Temperature")
Dim comObj = Activator.CreateInstance(objType)
Microsoft.VisualBasic.CallByName(comObj, "Celsius", CallType.Let, 100)
Dim f As Double = CDbl(Microsoft.VisualBasic.CallByName(comObj, "GetFahrenheit", CallType.Method, Nothing))
Console.WriteLine(f) ' shows 212
Code Block 3 (doesn't work)
Option Explicit On
Option Strict On
...
Dim objType = Type.GetTypeFromProgID("PhysServer.Temperature")
Dim comObj = Activator.CreateInstance(objType)
Dim f As Double = CDbl(objType.InvokeMember("GetFahrenheit", Reflection.BindingFlags.InvokeMethod, Nothing, comObj, Nothing))
Console.WriteLine(f) ' shows the default value of GetFahrenheit '
objType.InvokeMember("Celsius", Reflection.BindingFlags.SetField Or Reflection.BindingFlags.InvokeMethod, Nothing, comObj, New Object() {100}) ' throws exception: Number of parameters specified does not match the expected number '
f = CDbl(objType.InvokeMember("GetFahrenheit", Reflection.BindingFlags.InvokeMethod, Nothing, comObj, Nothing))
Console.WriteLine(f)
I understand Code Block 1 and Code Block 2. However, how could I use set a COM object using reflection like Code Block 3? By some reasons, I cannot use Code Block 1 and Code Block 2. So the only way is Code Block 3... but it doesn't work.
Does anyone know the solution of Code Block 3? Thanks!
Try this:
objType.InvokeMember("Celsius", Reflection.BindingFlags.SetProperty Or ...
instead of SetField.
comObj is a Runtime-Callable Wrapper, and Celsius will be a Property thereof, not a field.
Its also possible you may need to specify the BindingFlags.Instance flag as well.

Reflection on structure differs from class - but only in code

Code snippet:
Dim target As Object
' target gets properly set to something of the desired type
Dim field As FieldInfo = target.GetType.GetField("fieldName", _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(target,newValue)
This snippet works perfectly IF target is set to an instance of a CLASS.
However, if target is set to an instance of a STRUCTURE, the code does not actually change the value of the field. No error, but the value remains unchanged.
And, oddly, if I'm stepping through code, watch the SetValue fail to do anything, and immediately go to the Immediate window and type exactly the same SetValue operation, that works.
Any suggestions on what's going on and how to actually change the field IN CODE?
Edit:
Per request from Jon Skeet, actual code:
Private Shared Function XmlDeserializeObject(ByVal objectType As Type, _
ByVal deserializedID As String) As Object
Dim result As Object
result = CreateObject(objectType)
mXmlR.ReadStartElement()
Do While mXmlR.IsStartElement _
AndAlso mXmlR.Name <> elementItem
Dim field As FieldInfo = result.GetType.GetField(FullName, _
BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
field.SetValue(result, XmlDeserialize(field.FieldType))
Loop
Return result
End Function
External variables and called routines:
* mXmlR is an XmlTextReader, and is properly initialized and positioned (else this would not work on classes)
* CreateObject works (ditto)
* XmlDeserialize mostly works, and at the point in question is handling an integer just fine. The only known problem is with structures.
As for how I'm checking the value, I'm mostly looking at the Locals window, but I've also used print statements in the Immediate window, and I'm running an NUnit test which is failing because of this problem - while the equivalent test with a class, rather than a structure, passes.
Here's the test.
<Serializable()> Private Structure SimpleStructure
Public MemberOne As Integer
End Structure
<Test()> Sub A016_SimpleStructure()
Dim input As New SimpleStructure
input.MemberOne = 3
Dim st As String = Serialize(input)
Debug.Print(st)
Dim retObject As Object = Deserialize(st)
Assert.IsNotNull(retObject)
Assert.IsInstanceOfType(GetType(SimpleStructure), retObject)
Assert.AreEqual(input.MemberOne, DirectCast(retObject, SimpleStructure).MemberOne)
End Sub
Working with your original sample, I agree that it works in C# but not in VB! If you use Reflector or ILDasm you will see that the call to Field.SetValue(target, ...) is actually compiled (in VB) as:
field.SetValue(RuntimeHelpers.GetObjectValue(target), ...)
GetObjectValue "Returns a boxed copy of obj if it is a value class; otherwise obj itself is returned." I.e. the value is being set on a copy of your struct!
This link gives the explanation (such as it is). The workaround is to declare target as System.ValueType instead of Object. I'm not sure if that actually helps in your real-life code: you may need a messy type test to be able to handle value types separately from reference types.
The problem is that VB makes a copy of the object and the setvalue instruction applies to the copy, but not to the object itself. The workaround is to restore the changes to the original object through an auxliar var and the CType function. In the following example, we want to set the country field of the champion var to Spain (champion is a *St_WorldChampion* structure). We make the changes in the x var, an then we copy them to the champion var. It works.
Public Structure St_WorldChampion
Dim sport As String
Dim country As String
End Structure
Sub UpdateWorldChampion()
Dim champion As New St_WorldChampion, x As ValueType
Dim prop As System.Reflection.FieldInfo
' Initial values: Germany was the winner in 2006
champion.country = "Germany"
champion.sport = "Football"
' Update the World Champion: Spain since 2010
x = champion
prop = x.GetType().GetField("country")
prop.SetValue(x, "Spain")
champion = CType(x, St_WorldChampion)
End Sub
Well, you haven't shown all your code - in particular, where you're setting target and how you're checking the value of the field afterwards.
Here's an example which shows it working fine in C# though:
using System;
using System.Reflection;
struct Foo
{
public int x;
}
class Test
{
static void Main()
{
FieldInfo field = typeof(Foo).GetField("x");
object foo = new Foo();
field.SetValue(foo, 10);
Console.WriteLine(((Foo) foo).x);
}
}
(I'm pretty sure the choice of language isn't relevant here, but with more code we could tell for certain.) My strong suspicion is that you're doing something like:
Foo foo = new Foo();
object target = foo;
// SetValue stuff
// What do you expect foo.x to be here?
The value of foo in the snippet above won't have changed - because on the second line, the value is copied when it's boxed. You'd need to unbox and copy again afterwards:
foo = (Foo) target;
If that's not it, please show a short but complete program which demonstrates the problem.
Hi I made this function using christian example, hope it helps.
This Function uses Properties which also are affected
''' <summary>
''' Establece el -valor- en la -propiedad- en el -objeto-
''' Sets Value in Objeto.[propertyname]
''' </summary>
''' <param name="objeto">Object where we will set this property</param>
''' <param name="Propiedad">Name of the property</param>
''' <param name="valor">New Value of the property</param>
''' <returns>Object with changed property</returns>
''' <remarks>It works on structures!</remarks>
Function Establecer_propiedad(objeto As Object, Propiedad As String, valor As Object) As Object
'Check arguments
If objeto Is Nothing Then Throw New ArgumentNullException("Objeto")
If String.IsNullOrWhiteSpace(Propiedad) Then Throw New ArgumentNullException("Propiedad")
'Get the object type
Dim t As Type = objeto.GetType
'Get the propertyInfo by its name
Dim prop As PropertyInfo = t.GetProperty(Propiedad)
'Check if the property exist
If prop Is Nothing Then Throw New InvalidOperationException("Property does not exist")
If Not prop.CanWrite Then Throw New InvalidOperationException("Property is read only")
'Determine if it is a class or a structure
If Not t.IsValueType Then ' (it is a reference value)
'Set without troubles
If prop.CanWrite Then prop.SetValue(objeto, valor)
'Return object
Return objeto
Else '(It is a structure)
'Create a box using a valuetype
'It doesnot work in object
Dim Box As ValueType
'Set item in box
Box = objeto
'Set value in box
prop.SetValue(Box, valor)
'Return box
Return Box
End If
End Function