I have the following C# code which compiles correctly:
private string formatterCSharp(int number)
{
return "n" + number;
}
private void testInCSharp()
{
IEnumerable<int> list = new List<int>();
IEnumerable<string> formatted = list.Select(formatterCSharp);
}
As you can see, formatted should contain the contents of list, with formatterCSharp applied to each.
When I try to replicate this code in VB, I come up with this:
Private Function formatterVisualBasic(ByVal number As Integer) As String
Return "n" + number
End Function
Private Sub testInVB()
Dim list As IEnumerable(Of Integer) = New List(Of Integer)
Dim formatted As IEnumerable(Of String) = list.Select(formatterVisualBasic)
End Sub
However I get two compilation errors on the Select statement in VB.
BC30455
Argument not specified for parameter 'number' of 'Private Function formatterVisualBasic(number As Integer) As String'.
BC30518
Overload resolution failed because no accessible '[Select]' can be called with these arguments:
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Try
Dim formatted As IEnumerable(Of String) = list.Select(AddressOf formatterVisualBasic)
In VB.net you can't specify function name to pass it like that.
https://msdn.microsoft.com/en-us/library/y72ewk2b.aspx
Okay first off the select query is formed differently in VB.NET, here's how it should look:
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic(mynumber))
Secondly, if you want to get the same exception as in your C# code, you must add parentheses to the function call formatterVisualBasic, so that it is recognized as a function
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic())
Related
I'm having one method like the following, where Days is an Enum having values from Sunday to Saturday, Which I'm calling using reflection. I'm having lots of similar methods with different signatures And the input parameters are also dynamic which I'm getting as a JSON.
Public Sub ReflectionMethod(ByVal stringParam As String, ByVal enumParam As Days)
Console.WriteLine($"Executed:ReflectionMethod | stringParam : {stringParam}; enumParam : {enumParam}")
End Sub
The following code is used to parse the JSON and call the method using reflection.
Dim inputJson As String = "['Sample Input',2]"
Dim lstObjects As List(Of Object) = JsonConvert.DeserializeObject(Of List(Of Object))(inputJson)
Dim method As MethodInfo = consoleObject.GetType.GetMethod("ReflectionMethod")
method.Invoke(consoleObject, lstObjects.ToArray)
Throwing the following error while executing the above code, During debugging noticed that method.Invoke() method is getting failed nothing wrong with the deserialization.
Exception: Object of type 'System.Int64' cannot be converted to type 'VBConsole.Days'.
Please note: This is not the actual code that I'm using, just created a console application for easy debugging, and an answer in C# is also appreciated, That's why tagged it to c#
In order to pass your array of JSON values into a method to be invoked by reflection, you must deserialize each JSON value to the required argument type. This can be done as follows:
Public Sub InvokeMethodWithJsonArguments(ByVal consoleObject As Object, ByVal methodName As String, ByVal inputJson As String)
Dim lstObjects As List(Of JToken) = JsonConvert.DeserializeObject(Of List(Of JToken))(inputJson) ' Deserialize to an intermediate list of JToken values
Dim method As MethodInfo = consoleObject.GetType.GetMethod(methodName)
Dim parameterTypes = method.GetParameters().Select(Function(p) p.ParameterType) ' Get the required type of each argument.
Dim args = lstObjects.Zip(parameterTypes, Function(first, second) first.ToObject(second)).ToArray() ' Deserialize each JToken to the corresponding required type
method.Invoke(consoleObject, args) ' Invoke with the deserialized arguments.
End Sub
Notes:
I am assuming you need a solution that does not hardcode the argument types.
MethodInfo.GetParameters() returns an array of parameter information matching the signature of the method to be invoked.
JToken.ToObject(Type) deserializes a JSON token to a type specified in runtime.
Demo fiddle here.
I have a method that accepts Enum values as parameter, this answer is my reference. However I cant seem to make it work.
Enums
Public Enum FieldTypes
db_Alpha = 0
db_Memo = 1
db_Numeric = 2
End Enum
Public Enum SubTypes
st_None = 0
st_Phone = 35
st_Percentage = 37
st_Address = 63
st_Link = 66
End Enum
Method
Public Sub Foo(Of T)(ByVal Param1 As String, ByVal EnumParam As T)
Dim param As Type = GetType(T)
If param.IsEnum Then
Dim x = param.GetEnumValues()
End If
'Running some codes here
End Sub
Main
Public Sub Test()
Foo("TestName",FieldTypes.db_Memo)
End Sub
Im getting all the values of the type of Enum that was passed as a parameter.
I would want to reduce Method overloading and learn about generics here. Accepting C# and VB.net suggestions.
How could I get the value of of the Enum item passed?
How can I restrict this, that only the two types of Enum written is accepted?
Since an Enum is always a value type you can restrict your function "Foo" to only accept value types.
Public Sub Foo(Of T As Structure)(ByVal Param1 As String, ByVal EnumParam As T)
You could also restrict your generic function to only accept reference types
Public Sub Test(Of T As Class)(ByVal param As T)
or only accept types that implement a specific interface
Public Sub Test(Of T As IDispose)(ByVal param As T)
You can even concatenate those conditions
Public Sub Test(Of T As Class, IDispose, IEnumerable)(ByVal param As T)
Inside your function "Foo" you can check, that T is of the correct type like this:
If Not GetType(T).Equals(GetType(FieldTypes)) AndAlso _
Not GetType(T).Equals(GetType(SubTypes)) Then
Throw New Exception("Enum type not supported")
End If
To get the numeric value of "EnumParam" you can convert it to Integer or whatever else numeric type your enumeration is based on:
Dim x As Integer = Convert.ToInt32(EnumParam)
(I am a ASP.NET programmer that uses mostly VB but can also program in C#)
I like to have a list(t) full with keywords and function names.
For example:
Dim MyList as new List(of MyFunctions)
MyList.add(new Myfunction("descr","DBGetDescription()"))
MyList.add(new Myfunction("Name","DBGetName()"))
MyList.add(new Myfunction("create","DBGetCreateTS()"))
Public Class Myfunctions
Public code as String
Public function as String
Public Sub new(in_code as String, in_function as String)
code = in_code
function = in_function
End Sub
End Class
'what do i want to search for:
Dim ToDo as String = "descr"
'search for it
Dim ThisFunction as MyFunction = MyList.find(function(x) x.code = ToDo)
'answer in 'thisfunction' gives me the name of the function i need to use
Dim FunctionIWantToUse = ThisFunction.function
(the DBGetDescription, DBGetName and DBGetCreateTS are existing routines)
I know the name of the function(routine) I want to start but it is inside a variable.
Is it possible in VB (or in C#) to start the function?
Is there a work around?
M.
You can define a delegate which is basically a reference to a function. This allows you to map "names" to actual functions, to make it possible to call a function from a string name.
Delegate Function MyDelegateType() as string
Sub Main
' Create a dictionary mapping string names to the corresponding function.'
' Ive used a dictionary in my sample code, as its more straightforward, '
' but it would be trivial to wrap the name/function tuple up in a class and use a list'
Dim MyList as new Dictionary(of string, MyDelegateType)
MyList.Add("descr", addressof DBGetDescription)
MyList.add("Name", addressof DBGetName)
MyList.add("create", addressof DBGetCreateTS)
' Get the function for a key, and call it.'
Console.WriteLine(MyList.Item("descr")())
End Sub
Function DBGetDescription() as string
return "MyDescription"
End Function
Function DBGetName() as string
return "MyName"
End Function
Function DBGetCreateTS() as string
return "MyCreateTS"
End Function
I'm trying to create a class thats holds two (or more) generic delegates as properties. This way I can pass the object to a method and use only one parameter on that method. The problem is that I get a warning to specify a type, but I don't know the type yet. The whole point is to defer type declaration until the object gets instantiated.
Here's some dummy code dummy code to show what I want to do.
Public Function Method1()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type string
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method2()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type Integer
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method3(container as Container)
//execute type specific code and the delegates
//Throw exception when type is not supported (yet)
end Function
public Class Container
Property Property1 as MyDel(of T)
Property Property2 as MyDel(of T)
end Class
Public delegate function Mydel(of T)()
That's it. The point of this being that when adding new information/functionality to the system it is easy to create a new method called Method4, which uses arguments as needed (and of course create the method that will be executed as delegate).
I would like to put two delegates in one class because their is a connection between them and things belonging together should be put together in one class; just like a Person class can hold a name and address.
This code doesn't work, because Container needs a Type T, which means Method3 needs a specific type, but i can't do that because then I can't call Method3 with different arguments as I showed. I tried using a wrapper class, but a type specification is needed all the time.
I know you can do this
Public Property TestProp() As [Delegate]
Get
End Get
Set(ByVal Value As [Delegate])
End Set
End Property
[source:http://www.xtremedotnettalk.com/showthread.php?t=96800]
But that is not generic. What am I missing?
BTW, this is an optimization for code calling Method3 with two arguments. That seemed to work fine. Then I thought trying to use only one argument, which would make the system easier to understand.
My old code looks something like this:
Public function OldMethod1()
Dim del1 as Mydel(of Integer) = AddressOf SomeMethod
Dim del2 as MyDel(of String) = AddressOf SomeOtherMethod
oldMethod3(del1, del2)
end Function
Public function OldMethod2()
Dim del1 as Mydel(of String) = AddressOf AnOtherMethod
Dim del2 as MyDel(of String) = AddressOf AgainSomeOtherMethod
oldMethod4(del1, del2)
end Function
Public Function oldMethod3(del1 as Mydel(of Integer), del2 as Mydel(of string))
//execute delegates
end Function
Public Function oldMethod4(del1 as Mydel(of string), del2 as Mydel(of string))
//execute delegates
end Function
When reading this code I saw that it is only the type of the parameter the determines the execution flow. So if you can determine the subtype of the generic you know what to do. That seemed to be possible (use typeOf and or GetType), so the next thing to do is to create a generic parameter object that could be a substitute for 'del1' (I now realize I took it even took one step further and created the Container class to hold both the parameters).
Any answer in C# or VB.net will do. I use both.
The question is a little confusing, so this may not be an answer, but it's too long for a comment! I think what you want to do is use Action(Of T) as the type for Container.Property1 and Container.Property2.
Something like this (this will run in LINQPad using Language: VB Program):
Sub Main
Dim c1 = New Container(Of String, String)()
c1.Property1 = AddressOf Method1
c1.Property2 = AddressOf Method1
Method3(c1)
Dim c2 = New Container(Of String, Integer)()
c2.Property1 = AddressOf Method1
c2.Property2 = AddressOf Method2
Method3(c2)
End Sub
Public Sub Method1(x As String)
Console.WriteLine("Method1: {0}", x)
End Sub
Public Sub Method2(x As Integer)
Console.WriteLine("Method2: {0}", x)
End Sub
Public Sub Method3(Of T1, T2)(container as Container(Of T1, T2))
Console.WriteLine("Method3 got {0}, {1}", GetType(T1), GetType(T2))
' Not sure how you would actually call the delegates
If GetType(T1) = GetType(String) Then
container.Property1.DynamicInvoke("Hello")
ElseIf GetType(T1) = GetType(Integer) Then
container.Property1.DynamicInvoke(123)
End If
If GetType(T2) = GetType(String) Then
container.Property2.DynamicInvoke("World")
ElseIf GetType(T2) = GetType(Integer) Then
container.Property2.DynamicInvoke(456)
End If
End Sub
Public Class Container(Of T1, T2)
Public Property Property1 As Action(Of T1)
Public Property Property2 As Action(Of T2)
End Class
This produces the following results:
Method3 got System.String, System.String
Method1: Hello
Method1: World
Method3 got System.String, System.Int32
Method1: Hello
Method2: 456
Is it possible to translate the following C# code into VB.NET, using VB 9.0?
delegate Stream StreamOpenerDelegate(String name);
void Exec1()
{
WorkMethod( x => File.OpenRead(x));
}
void Exec2()
{
StreamOpenerDelegate opener = x => return File.OpenRead(x) ;
WorkMethod(opener);
}
Can I do something like this?:
Private Delegate Function StreamOpenerDelegate(ByVal name As String) As Stream
Private Sub WorkMethod(ByVal d As StreamOpenerDelegate)
''
End Sub
Private Sub Exec1()
Me.WorkMethod(Function (ByVal x As String)
Return File.OpenRead(x)
End Function)
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (ByVal x As String)
Return File.OpenRead(x)
End Function
Me.WorkMethod(opener)
End Sub
I'm trying to write some documentation, but I don't know VB syntax. Often I use Reflector to translate it, but I'm not sure it's working in this case. I'm also not clear on where I would need line continuation characters.
ANSWER
In VB9, it's not possible to have multi-line lambdas (or Sub lambdas, which I did not ask about). In VB9, all lambdas return a value, and must be a single expression. This changes in VB10. VB10 will allow the above syntax, but VB9 will not. In VB9, if the logic involves multiple code lines, it must not be a lambda; you must put it into a named Function and reference it explicitly. Like this:
Private Delegate Function StreamOpenerDelegate(ByVal name As String) As Stream
Private Sub WorkMethod(ByVal d As StreamOpenerDelegate)
''
End Sub
Function MyStreamOpener(ByVal entryName As String) As Stream
'' possibly multiple lines here
Return File.OpenRead(entryName)
End Function
Private Sub Exec1()
Me.WorkMethod(AddressOf MyStreamOpener)
End Sub
site: Mike McIntyre's blog
This should work:
Private Sub Exec1()
Me.WorkMethod(Function (x) File.OpenRead(x))
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (x) File.OpenRead(x)
Me.WorkMethod(opener)
End Sub
You need the line continuation character to split a single line statement into multiple lines, like so:
Private Sub Exec1()
Me.WorkMethod(Function (x) _
File.OpenRead(x))
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (x) _
File.OpenRead(x)
Me.WorkMethod(opener)
End Sub
In any case, in VS2010 there is implicit line continuation after certain characters. So I wouldn't worry about it too much.