I updated an AutoMapper V3.3.1 to V6.1.1 and much to my surprise, after putting all CreateMaps() in a profile, it actually worked perfectly right out of the gate – almost scary for me.
The problem that I am having is that it IS working with the code below as is suggested in the AutoMapper documentation:
Private Sub InitiatizeAutoMapper()
Mapper.Initialize(Function(cfg)
cfg.AddProfile(Of MappingProfile)()
End Function)
End Sub
But the code issues a warning:
Warning BC42105 Function '<anonymous method>' doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used.
If I add a Return to the Lambda like:
Private Sub InitiatizeAutoMapper()
Mapper.Initialize(Function(cfg)
Return cfg.AddProfile(Of MappingProfile)()
End Function)
End Sub
I then get the following error:
Error BC30518 Overload resolution failed because no accessible 'Initialize' can be called with these arguments:
'Public Shared Overloads Sub Initialize(config As Action(Of IMapperConfigurationExpression))': Expression does not produce a value.
'Public Shared Overloads Sub Initialize(config As Action(Of IMapperConfigurationExpression))': Expression does not produce a value.
'Public Shared Overloads Sub Initialize(config As MapperConfigurationExpression)': Lambda expression cannot be converted to 'MapperConfigurationExpression' because 'MapperConfigurationExpression' is not a delegate type.
Now - if I make it a Sub instead of a function, it all works well with no errors like this:
Private Sub InitiatizeAutoMapper()
Mapper.Initialize(Sub(cfg)
cfg.AddProfile(Of MappingProfile)()
End Sub)
End Sub
I know that this will probably be a head-slapper but I am trying to follow the documentation and am afraid to release this into production in that I might be missing something.
EDIT:
I chose to break down the multiline lambda to make it a little easier for me, which also avails IntelliSense options quite handily for future enhancement. While it is probably not the "coolest" code, I find it very readable.
Private Sub InitiatizeAutoMapper()
Dim config As New Configuration.MapperConfigurationExpression : With config
.AddProfile(Of MappingProfile)()
End With
Mapper.Initialize(config)
End Sub
The whole point of a Function is that it returns something. In your first code snippet, what is your anonymous function returning? It isn't returning anything because it has no Return statement. There's no point arbitrarily adding a Return statement if you don't actually have anything to return and you can't return the result of AddProfile if that doesn't return anything itself. Basically, your lambda doesn't return anything and what do we call a method that doesn't return anything? It's a Sub.
You'll notice in the error message you posted that the Initialize method overloads that take a delegate as an argument are expecting an Action, not a Func. Action delegates are created with a Sub while a Function is used to create a Func delegate.
Related
I am using Dapper to query a flat list of items from a database, into a POCO class as follows:
Public Class Node
Public Property Name As String
Public Property ParentNodeName As String
Public Property Children As IEnumerable(Of Node)
End Class
I am trying to use the accepted answer to this question, in order to create a tree out of the flat list.
The only caveat is that I am using VB.NET.
I have tried it a straightforward port of the C# solution:
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
but it does not compile with the error
Error BC30452 Operator '=' is not defined for types 'List(Of Node)' and 'List(Of Node)'.
The = symbol is interpreted as an equality operator, while I meant to use the assignment operator.
I have pasted the C# code into the telerik converter, and the converted code is:
Private Shared Function BuildTree(ByVal items As List(Of Category)) As IEnumerable(Of Category)
items.ForEach(Function(i) CSharpImpl.__Assign(i.Categories, items.Where(Function(ch) ch.ParentId = i.Id).ToList()))
Return items.Where(Function(i) i.ParentId Is Nothing).ToList()
End Function
Private Class CSharpImpl
<Obsolete("Please refactor calling code to use normal Visual Basic assignment")>
Shared Function __Assign(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
It uses an helper class to solve this issue, but suggests a refactor to avoid this.
Hence the questions:
Is there a general way to disambiguate equality = and assignment = in VB.NET, without resorting to an helper class and a specific function to assignement
In this specific case, is there a simple refactor I can use to get rid of the issue?
That's because of VB.Net distinction between functions and subroutines.
Instead of
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
use
nodes.ForEach(Sub(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
When you use Function, the lambda expression is expected to return a value; and in your case it looks like it wants to return a boolean.
But you want to use a lambda expression that does not return anything (in your case, you want an assignment), you have to use Sub.
In JavaScript, and other Object oriented languages, you can instantiate a new instance of a class, and use a method of the class instance in the same line. For example:
class customClass {
method(){
return "hello"
}
}
var s = (new customClass).method() // s = "hello"
If I try the same in VBA:
s = (new customClass).method()
I get a Compile error: Expected: end of statement, while highlighting the . after customClass).
If you like you can do the following:
Dim c as new customClass: s=c.method()
however in this case, in my opinion, it is not clear that s is being set at all making code more difficult to manage.
Is there an alternative method which makes the line clearer to understand, while still being quick and easy to write?
Sure you can.
With New Collection: .Add 42: Debug.Print .Count: End With
I'd probably want to chew the head off whoever put that in production code, but sure you can.
Doesn't mean you should.
Note that for some reason you don't get IntelliSense for With block variable references in such inline With blocks.
May or may not have something to do with VBA code shouldn't be written like this.
If the method you want to invoke doesn't alter global or instance state, consider specifying a VB_PredeclaredId module attribute on your class instead (export the module, edit its header in Notepad, re-import the module).
That way this code becomes legal, and looks much more VBA-idiomatic:
SomeCustomClass.SomeMethod
The mechanics are identical to those involved when people do UserForm1.Show off the default instance, without creating a New object.
You can pass a new instance of a class to the CallByName Function and have it access the method for you.
Dim s As String: s = CallByName(New customClass, "method", VbMethod)
Currently I have thought of 2 methods of solving the problem.
The first method I came up with is a bit hacky, but it does the trick:
s = Array(new someClass)(0).method()
However in general this method is quite confusing and difficult to read... So instead I figured you could just use a helper function instead:
function proxy(a as variant) as variant
if isObject(a) then
set proxy = a
else
proxy = a
end if
end function
sub textProxy()
s = proxy(new someClass).method()
debug.print s
end sub
It is a shame however that you lose the intelisense in both of these examples... But I guess that's more of a problem with the VBE than the methods themselves.
So I have the following line of code in c# to register AutoMapper with SimpleInjector:
container.Register(() => config.CreateMapper(container.GetInstance));
For context, config.CreateMapper() looks like this:
public IMapper CreateMapper(Func<Type, object> serviceCtor);
While container.GetInstance looks like this:
public object GetInstance(Type serviceType);
The first line of code I posted works in C# because config.CreateMapper takes in a function that takes a type of Type and returns a type of object. Exactly what container.GetInstance does.
When I try to do this same line in VB, as:
container.Register(Function() config.CreateMapper(container.GetInstance))
It gives an error Type arguments for 'Public Function Container.GetInstance(Of TService)()' cannot be inferred from the usage. Try specifying type arguments explicitly. It is giving this error because container.GetInstance has an overload that looks like this:
public TService GetInstance<TService>() where TService : class;
Why does the VB try to use the wrong overload of GetInstance and how can I force it to use the correct one?
Use AddressOf for getting function reference.
container.Register(Function() config.CreateMapper(AddressOf container.GetInstance))
In C# function with parenthesis executes it, function without parenthesis will return reference to the function.
In VB.NET parenthesis in function are optional - function will be executed anyway. So for referencing a function you should use AddressOf operator.
AddressOf Operator
Use the AddressOf keyword before the function parameter to define this as Parameter as function pointer.
AddressOf ist the same as # keyword in C#. The keyword is implicit used in your c# sample.
I want to experiment with a simple bit of code found here:
http://msdn.microsoft.com/en-au/library/ff664617%28v=pandp.50%29.aspx
But I cannot get it to compile, I must be doing something wrong. About a third of the way down the page it gives a code sample like this:
'Usage exManager.Process(Function() method-name(param1, param2), _
"Exception Policy Name")
But if I enter the following code:
Dim exManager As ExceptionManager
exManager = EnterpriseLibraryContainer.Current.GetInstance(Of ExceptionManager)()
exManager.Process(Function() TestSub(), "Exception Policy Name")
I get an error on the third line that says:
Overload resolution failed because no accessible 'Process' can
be called with these arguments:
'Public Overridable Function Process(Of TResult)(action As System.Func(Of TResult), policyName As String) As TResult': Cannot
refer to an instance member of a class from within a shared method or
shared member initializer without an explicit instance of the class.
'Public Overridable Function Process(Of TResult)(action As System.Func(Of TResult), policyName As String) As TResult': Data
type(s) of the type parameter(s) cannot be inferred from these
arguments. Specifying the data type(s) explicitly might correct this
error.
etc.
Even if I try to modify the lambda like this:
exManager.Process(Function() Dim A As Integer=6, "Exception Policy Name")
I get a similar error.
Any comments would be appreciated.
UPDATE:
Note I am compiling for .NET Framework 4 Client Profile
You are using the Process(Of TResult) overload when you call exManager.Process(Function() TestSub(), "Exception Policy Name").
The method is generic and expects and type argument (TResult), which in a lot of cases can be inferred by the compiler. Now the exception tells you that, in your case, the compiler can infer TResult.
I guess your TestSub is really a Sub and therefore has no return value which the compiler could use to infer TResult.
So use the non-generic Process method by either using Sub instead of Function
exManager.Process(Sub() TestSub(), "Exception Policy Name")
or simply use AddressOf:
exManager.Process(AddressOf TestSub, "Exception Policy Name")
In order to return a value from a VB.NET function one can assign a value to the "Functions Name" or use "return value."
I sometimes see these inter-mixed in the same function. Personally, I prefer the return.
My question is, what is the internal difference, if any, between the two?
The difference is that they DO DIFFERENT THINGS!
'Return value' does 2 things:
1. It sets the function return value at that point
2. It immediately exits the function
No further code in the function executes!
'Functionname = value' does 1 thing:
1. It sets the function return value at that point
Other code in the function continues to execute
This enables additional logic to refine or override the function return value
Huge difference folks. Remember it's not all about state, it's also about flow.
Let's take a look... Oddly the "functionName =" generates less IL?
Code:
Public Function Test() As String
Test = "Test"
End Function
Public Function Test2() As String
Return "Test"
End Function
IL:
.method public static string Test() cil managed
{
.maxstack 1
.locals init (
[0] string Test)
L_0000: nop
L_0001: ldstr "Test"
L_0006: stloc.0
L_0007: ldloc.0
L_0008: ret
}
.method public static string Test2() cil managed
{
.maxstack 1
.locals init (
[0] string Test2)
L_0000: nop
L_0001: ldstr "Test"
L_0006: stloc.0
L_0007: br.s L_0009
L_0009: ldloc.0
L_000a: ret
}
There is probably no difference. IIRC, the compiler generated IL converts them both into Return statements unless there is additional usage of a _returnValue variable.
The readability of the FunctionName assignment is poor in my opinion, and an example of a bad VB6 habit. I prefer the _returnValue (NOT RETVAL) variable method also.
Doing the following is only provided for Visual Basic 6.0 developers to easily port code over:
Public Function MyFunction() As String
MyFunction = "Hello"
End Function
I would definitely not recommend keeping doing it if your project includes anyone who hasn't worked with Visual Basic 6.0, as this syntax will be confusing.
When Tools/Options/Text Editor/All Languages/Code Lens is activated, the Reference Count shows above each Sub, Function, or Property statement.
"Return Value" seems better than "assigning a value to the Functions Name". In the latter case, "Code Lens" produces an inflated Reference Count.
' Code Lens reports "0 references" here for Sub Rosa().
Public Sub Rosa()
Diagnostics.Debug.WriteLine(Test())
Diagnostics.Debug.WriteLine(Test2())
End Sub
' Code Lens reports "2 references" here for Function Test().
Public Function Test() As String
Test = "Test" ' Code Lens counts this as a reference.
End Function
' Code Lens reports "1 reference" here for Function Test2().
Public Function Test2() As String
Dim strTest2 as String = "Test"
Return strTest2 ' Code Lens does NOT count this as a reference.
End Function
99 times out of 100 I'll use "return value".
Every once in a while I'll have a function where the other type not only allows me to save a variable declaration, but do it in a way that actually significantly clarifies the function. Usually this happens when I would want to name the return value the same as the function anyway, and often these are recursive functions; something about that construct lends it to the implicit return variable. However, that scenario is extremely rare. I don't know if I have any functions using implicit return variables at all in my current project.
Having read that the Return Value syntax was the One True .NET Way Of Doing Things I thought "OK, we'll do it that way then". Then I wrote a function which I knew, hand on heart KNEW, returned either a value from a Return statement or alternatively an Exception under all circumstances, and still got a compiler warning that the function "doesn't return a value on all paths".
Thankfully I came across the Stack Overflow question How can I make this function not generate a “doesn't return a value on all paths” warning? which explained why; adding a default value assignment to the procedure name at the head of the function prevented the warning in my case as well.
Consequently, even though I'll continue to use the Return Value syntax simply for the sake of syntax consistency, I'll also be assigning a default value to the function name just to prevent the possibility of cluttering up the compile process with bogus warnings.
its quite handy when working with 3rd party factories(_hsf), you can avoid declaring return variables
Public Function CreateExtremum(iShape As INFITF.Reference, iDir1 As HybridShapeTypeLib.HybridShapeDirection, iSide1 As Integer, iDir2 As HybridShapeTypeLib.HybridShapeDirection, iSide2 As Integer, iDir3 As HybridShapeTypeLib.HybridShapeDirection, iSide3 As Integer) As HybridShapeTypeLib.HybridShapeExtremum
CreateExtremum = _hsf.AddNewExtremum(iShape, iDir1, iSide1)
CreateExtremum.Direction2 = iDir2
CreateExtremum.ExtremumType2 = iSide2
CreateExtremum.Direction3 = iDir3
CreateExtremum.ExtremumType3 = iSide3
CreateExtremum.Compute()
End Function