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
Related
(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
We had the need to create the following object, basically a way to pass in integer or strings and fetch these back out as either an integer or a zero-padded string:
Public Class DOC_NUMBER
Private m_DOC_NUMBER As Integer = 0
Public Sub New(ByVal DOC_NUMBER As Integer)
m_DOC_NUMBER = DOC_NUMBER
End Sub
Public Sub New(ByVal DEPOT_CODE As String)
Dim ParseInput As Integer = 0
If Integer.TryParse(DEPOT_CODE, ParseInput) = False Then
m_DOC_NUMBER = 0
Else
m_DOC_NUMBER = ParseInput
End If
End Sub
Public Overrides Function ToString() As String
Return Right("0000000000" & m_DOC_NUMBER.ToString(), 10)
End Function
Public Function ToInteger() As Integer
Return m_DOC_NUMBER
End Function
End Class
An instance of this class can then be set as follows:
Dim DocumentID As New DOC_NUMBER("00123")
Dim DocumentID As New DOC_NUMBER(123)
This can then be assigned to other vars like this:
Dim NewDocumentID as Integer = DocumentID.ToInteger()
Dim NewDocumentID as String = DocumentID.ToString()
99% of the time however we'll be dealing with integers, so we'd like to make the .ToInteger() optional, for example:
Dim NewDocumentID as Integer = DocumentID
Obviously this currently gives the error "Value of type 'X.DOC_NUMBER' cannot be converted to 'Integer'"
How do we modify the class to automatically pass out an integer it's being assigned to an integer?
Examples in any .net language will be fine.
You can do what you want with implicit conversions. In C# that might look like this:
public static implicit operator string(DOC_NUMBER docNum)
{
// implement
}
public static implicit operator int(DOC_NUMBER docNum)
{
// implement
}
The VB.NET equivalent is the Widening Operator.
However, I'll point out that I find ToInteger() and ToString() (or as properties: AsInteger and AsString, perhaps) much clearer and simpler, and therefore better, than implicit conversions.
In debugging mode, if I hover over a predicate, what I see is just some type names and some non-understandable symbols. This makes it very difficult to debug a code, for example to know what predicate some variable is holding. I usually assign this predicates values using lambda expression. Is there any way to have some idea of what the predicates contain?
For example, if I have a Predicate<object> myPred variable or a List<Predicate<object>> predList variables, how can I debug what value myPred has or what predList contains at runtime?
You probably want Expression<Predicate<T>>. It can be converted to Predicate<T> in order to call it, but retains the information about the lambda structure.
[I haven't checked the C# IDE experience, but actually the VS2010 VB.NET experience.]
Either use Expression as #BenVoigt suggests, or don't use anonymous lambdas for your predicates: (VB.NET answer: Use Functions named by you and specify them with the AddressOf operator.)
C# answer is something like: declare explicit functions named by you and specify the function name when assigning the predicate.
Here is my test VB.NET code that confirms at least one way of dynamically creating predicates can be named successfully. In the VB.NET IDE these are easily seen by name.
Module Module1
Sub Main()
For i = 1 To 2
'Dim p As Predicate(Of Object) = Function(o) (o Is Nothing)
'Dim p As Predicate(Of Object) = AddressOf NamedPredicate
Dim p As Predicate(Of Object) = GeneratePredicate(i)
Dim q As Expressions.Expression(Of Predicate(Of Object)) = Function(o) (o IsNot Nothing)
If p(q) Then Console.WriteLine((q.Compile)(p))
Next
End Sub
Private Function NamedPredicate(ByVal o As Object) As Boolean
Return (o Is Nothing)
End Function
Private Function GeneratePredicate(ByVal i As Integer) As Predicate(Of Object)
Dim gp = New Reflection.Emit.DynamicMethod("DynPred" & i, GetType(Boolean), {GetType(Object)})
Dim mb = gp.GetILGenerator
mb.Emit(Reflection.Emit.OpCodes.Ldarg, 0)
mb.Emit(Reflection.Emit.OpCodes.Ldnull)
mb.Emit(Reflection.Emit.OpCodes.Ceq)
If i = 2 Then
mb.Emit(Reflection.Emit.OpCodes.Ldc_I4_0)
mb.Emit(Reflection.Emit.OpCodes.Ceq)
End If
mb.Emit(Reflection.Emit.OpCodes.Ret)
GeneratePredicate = DirectCast(gp.CreateDelegate(GetType(Predicate(Of Object))), Predicate(Of Object))
End Function
End Module
if you mean that in such example
new List<int>()
.Select(i => i + 1);
you would like to debug i + 1 part then you can put your mouse cursor (caret) somewhere at i + 1 and press F9 that will add a breakpoint in that expression
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.