VB.NET Function Return - vb.net

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

Related

Looking for lambda invocation shorthand documentation

I always took issue with the inability to var / Dim a variable implicitly when using a Using block to declare a data context which returns the initial value of the variable. For example,
Dim classifications As IEnumerable(Of RT_Classification)
Using dc As New MyDataContext
classifications = dc.RT_Classifications.OrderBy(Function(c) c.Order).ToList()
End Using
the type RT_Classification must be provided explicitly when the variable is declared. Compare with Using not limited to the query alone
Dim classifications = dc.RT_Classifications.OrderBy(Function(c) c.Order).ToList()
in which you get something implicit, analogous to var in c#. But the data context must then contain the declaration, and encompass the entire context of the variable classifications, which is undesirable.
I thought of using a lambda, which basically solves the problems
Dim classifications =
(Function()
Using dc As New MyDataContext
Return dc.RT_Classifications.OrderBy(Function(c) c.Order).ToList()
End Using
End Function).Invoke()
but seems a bit bulky. However, with knowledge of the () shorthand for .Invoke() and an IDE sugestion to remove ( ), I came up with this strange looking, but working code
Dim classifications =
Function()
Using dc As New MyDataContext
Return dc.RT_Classifications.OrderBy(Function(c) c.Order).ToList()
End Using
End Function()
Note the trailing End Function() which was new to me. My question is how long has this been available, and are there any downsides to using it, past potential readability issues?
Note that End Function() isn't a special kind of syntax. It is exactly equivalent to your call to f(), or if you were to wrap the entire expression in parentheses.
Prior to parsing the () shorthand, the compiler compiles the entire lambda expression, moves it to a separate method and replaces it in your code with a delegate pointing to that method. So when the compiler resumes parsing your code, all it sees is something along the lines of: ThisIsADelegate().
This becomes apparent if you decompile your application using a tool like ILSpy.
Original code:
Public Class TheseAreLambdas
Dim values As String() = {"This", "word", "is", "the", "longest"}
Dim classifications =
Function()
Return values.OrderBy(Function(s) s.Length).ToList()
End Function()
Public Sub DoSomething()
'I used DirectCast just to reduce mess in the decompiled code. Not necessary otherwise.
MessageBox.Show(DirectCast(classifications, List(Of String)).Count.ToString())
End Sub
End Class
Decompiled code:
public class TheseAreLambdas
{
private string[] values;
private object classifications;
public TheseAreLambdas()
{
/*
All variable initializations are automatically moved to the constructor.
*/
//Our array of values.
values = new string[5]
{
"This",
"word",
"is",
"the",
"longest"
};
//Our lambda expression, moved to a method called "_Lambda$__1", wrapped in
//a compiler-generated delegate and invoked on the spot (notice the parenthesis on the end).
classifications = new VB$AnonymousDelegate_1<List<string>>(_Lambda$__1)();
}
public void DoSomething()
{
MessageBox.Show(((List<string>)classifications).Count.ToString());
}
[CompilerGenerated]
private List<string> _Lambda$__1() //Our lambda expression used for the "classifications" variable.
{
return values.OrderBy((string s) => s.Length).ToList();
}
[CompilerGenerated]
private static int _Lambda$__2(string s)
{
return s.Length;
}
}

How can I disambiguate '=' symbol in VB.NET, in a lambda function

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.

Lambda on AutoMapper Initialization Shows Warning as Function but Fine as Sub

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.

VB.Net: Is there something to be gained by using the Function itself to hold the return value instead of declaring a local variable?

What's best practice (in VB.Net):
Function GetSomething() as String
GetSomething = "Here's your string"
End Function
or
Function GetSomething() as String
Dim returnString as String = "Here's your string"
Return returnString
End Function
Obviously, neither of these implementations make any sense, but they're just meant to illustrate my point. Is there anything to be gained by using GetSomething itself to store the return value instead of declaring returnString locally and then returning that (does it avoid having an extra string allocated/instantiated - and if so, are there any performance/memory benefits)?
Interesting question. I ran this:
Function GetSomething1() As String
GetSomething1 = "Here's your string"
End Function
Function GetSomething2() As String
Dim returnString As String = "Here's your string"
Return returnString
End Function
through IL DASM and here are the results:
Debug build:
.method public instance string GetSomething1() cil managed
{
// Code size 9 (0x9)
.maxstack 1
.locals init ([0] string GetSomething1)
IL_0000: nop
IL_0001: ldstr "Here's your string"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ret
} // end of method Form1::GetSomething1
.method public instance string GetSomething2() cil managed
{
// Code size 13 (0xd)
.maxstack 1
.locals init ([0] string GetSomething2,
[1] string returnString)
IL_0000: nop
IL_0001: ldstr "Here's your string"
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method Form1::GetSomething2
Release build:
.method public instance string GetSomething1() cil managed
{
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] string GetSomething1)
IL_0000: ldstr "Here's your string"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ret
} // end of method Form1::GetSomething1
.method public instance string GetSomething2() cil managed
{
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] string GetSomething2,
[1] string returnString)
IL_0000: ldstr "Here's your string"
IL_0005: stloc.1
IL_0006: ldloc.1
IL_0007: ret
} // end of method Form1::GetSomething2
You'll note that there are more operations in the debug build but not in the release build.
So to answer your question, it seems that declaring the variable costs a little bit extra in debug builds, which is often the case (optimization not turned on or is not as much optimized). But in the release build (as expected) the optimizer removes this unnecessary operation.
Try it both ways then use .Net Reflector to see if the generated IL is different.
I prefer having explicit exit points so I always use Return. Furthermore, I don’t like VB’s decision to use the function name as the name of the return variable (why not Result, like in Pascal?) because this makes it inconsistent when the function returns an array:
Function Foo() As Integer()
Foo = New Integer(2) { }
Dim a = Foo(1) ''// Recursive call or access to return value element?
End Function
Of course that’s a recursive call (otherwise those wouldn’t be possible any longer). But I’m very annoyed by such inconsistencies so I don’t use the syntax.
On the other hand, there’s the advantage of a standardized name for the return value holder, and that’s definitely an advantage over the Return method.
Either way, VB generates the same code.
You could use reflector to check out out, but, in all honesty, whatever performance penalty there might be for something like this would fall squarely under the heading of "Premature optimization" .
From a syntactic perspective, the {Return "a value"} syntax tends to encourage multiple exit points from the method, which is often seen as a bad practice. But if the method is small, then I don't see that code readability would suffer from it.
OTOH, the {var = value; Return Var} syntax tends to promote a single exit point at the end of the function.
The execution difference is essential nil, so it would depend on which is easier to understand and maintain. I personally assign a variable to the function name before an exit or use the return statement, rather than using the function name as a variable throughout the function. It seems a little odd or ambiguous to me if I use the function name as a variable throughout the function.
If I remember right, return() was added as the return statement to a function between VB6 and VB.Net, probably to make it more like other languages. This would explain why there are the two ways to do the same thing.
As mentioned in this answer and in my comments to this answer, one feature of the function name as return variable is that it can be accessed (and changed) in a Finally after a Return has been used.

Does Exit Function return false?

In VB.NET on a boolean function if you run an Exit Function line will it return false?
That is correct, with the caveat that in VB the function name can also be a variable that is returned. If you've previously set that to true, it will return true.
More completely, in VB.Net, if I have a boolean function Foo() defined like so:
Public Function Foo() As Boolean
'...
...the body of that function has an implied variable also named Foo that matches the return type of the function — Boolean in this case, but Object if the return type is omitted (you should be using Option Strict, which requires a return type). This implied variable is initialized to use the default value for that type.
If you fail to Return a value from the function, whether via Exit Function or simply by reaching the end, this implied variable is returned instead. Therefore, a Boolean function will return False if you Exit Function early without making other changes, because that is the default value in the implied variable used with the function. But you could also set that variable to True first if you wanted, and then Exit Function would cause it to return True instead.
These days people don't often use the implied variable, but there are situations where it can save you a few lines of code without costing anything in clarity.
Regardless if it does or not (the compiler gives a null-reference warning only), you should still explicitly return false, if only for readability.
As long as you have not set that function to True before you exit
I always do "Return True" or "Return False" to exit a method instead of the exit statement.