What is the VB equivalent of this C# syntax, dealing with delegates? - c#

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.

Related

How to unit test a private static/shared method?

As per this answer to Unit testing private methods in C# I am using a PrivateObject to unit test private methods. This generally works really well and is quite easy to use, however it doesn't appear to work if the method is Shared or Static.
I don't really want to make the method public (otherwise I wouldn't even be bothering with PrivateObjects but I'm failing to see another way.
Example vb methods in class being tested:
Private Sub SampleInstanceMethod(arg as Object)
'Do something
End Sub
Private Shared Sub SampleSharedMethod(arg as Object)
'Do something
End Sub
Unit test code
Dim fooBarPO = New PrivateObject(GetType(FooBar))
fooBarPO.Invoke("SampleInstanceMethod", {arg1}) ' Works
fooBarPO.Invoke("SampleSharedMethod", {arg1}) ' Doesn't work
Interested in answers in either C# or VB
You could use PrivateType and InvokeStatic for this:
Dim foo = New PrivateType(GetType(TestClass))
foo.InvokeStatic("SampleSharedMethod", {arg1})
If you want to pass parameters ByRef - for instance if the method under test looked something like this:
Private Shared Sub SampleSharedMethod(ByRef arg As String)
arg += "abc"
End Sub
You can use this overload of InvokeStatic to get the results back:
Dim foo = New PrivateType(GetType(TestClass))
Dim params() As Object = {"123"}
foo.InvokeStatic("SampleSharedMethod", params)
Dim updatedValue = params(0) ' This would be 123abc in this example

Linq Select compiling differently between C# and VB

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())

Var depending functions in vb.net

(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

creating a class that holds generic delegates

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

How to Convert this generic method from C# to VB.Net

I have the following code block in C#
private void Synchronize<T>(TextSelection selection, DependencyProperty property, Action<T> methodToCall)
{
object value = selection. GetPropertyValue(property) ;
if ( value != DependencyProperty. UnsetValue) methodToCall((T) value) ;
}
That I have converted to VB.
Private Sub Synchronize(Of T)(ByVal selection As TextSelection, ByVal [property] As DependencyProperty, ByVal methodToCall As Action(Of T))
Dim value As Object = selection.GetPropertyValue([property])
If value IsNot DependencyProperty.UnsetValue Then
methodToCall(DirectCast(value, T))
End If
End Sub
The calling method look like:
Synchronize(Of Double)(selection, TextBlock.FontSizeProperty, AddressOf SetFontSize)
Synchronize(Of FontWeight)(selection, TextBlock.FontSizeProperty, AddressOf SetFontWeight)
Synchronize(Of FontStyle)(selection, TextBlock.FontStyleProperty, AddressOf SetFontStyle)
Synchronize(Of FontFamily)(selection, TextBlock.FontFamilyProperty, AddressOf SetFontFamily)
Synchronize(Of TextDecorationCollection)(selection, TextBlock.TextDecorationsProperty, AddressOf SetTextDecoration)
My problem is with the DirectCast call; if my delegate argument can be a simple type (integer, double, etc) or an object. DirectCast doesn't like the simple data types an InvalidCastException is thrown when I try to cast to a double. Does anyone have a suggested solution to this problem? I've also tried TryCast, but it doesn't like my (of T) and says it must be class contstrained.
Thanks all!
Ryan
Try CType() instead of DirectCast().
Looking at the errors you're seeing, it sounds like you may need to add a constraint on your T handle limit it to a class in order for the TryCast to be used.
I'm not really familiar with VB's TryCast method (or DirectCast, for that matter), but something like this might help [note that (Of T) -> (Of T as Class) ]:
Private Sub Synchronize(Of T as Class)(ByVal selection As TextSelection, ByVal [property] As DependencyProperty, ByVal methodToCall As Action(Of T))
Dim value As Object = selection.GetPropertyValue([property])
If value IsNot DependencyProperty.UnsetValue Then
methodToCall(TryCast(value, T))
End If
End Sub
Use CType instead of TryCast/DirectCast. It should work like the casting in C# :
methodToCall(CType(value, T))
You can put multiple constraints on t
I believe the syntax is something like:
Private Sub Synchronize(Of T as {Class, Integer, Double})

Categories

Resources