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

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.

Related

Cast generic structure to other structure

I'm having trouble wrapping my head around the problem I'm having. I want to apply some general rules to structures, and as the type of them differs, I want to use a generic function to do this. My problem is that to manipulate the structure via methods only available with parameters of a specified type, I can not find a way to do so without extensive casting. See, for example, what steps are needed to specify that a DateTime value should always be specified as UTC:
Public Shared Function Sanitize(Of T As Structure)(retValue As T?) As T?
' If value is DateTime it must be specified as UTC:
If GetType(T) = GetType(DateTime) AndAlso retVal.HasValue Then
' To specify the value as UTC, it must first be casted into DateTime, as it is not know to the compiler that the type in fact IS
' DateTime, even if we just checked.
Dim retValAsObj = CType(retVal, Object)
Dim retValAsObjAsDateTime = CType(retValAsObj, DateTime)
Dim retValWithSpecifiedKind = DateTime.SpecifyKind(retValAsObjAsDateTime, DateTimeKind.Utc)
retVal = CType(CType(retValWithSpecifiedKind, Object), T?)
End If
Return retVal
End Function
Am I missing something? Casting four times for such a simple task seems to complex for me to be the best / simplest solution.
You can use extension methods
With extension method you don't need to check type and cast it.
With extension method you will have own method for every type - simple to maintain
With extension method you will have "readable" syntax
<Extension>
Public Shared Function Sanitize(Date? nullable) AS Date?
{
If nullable.HasValue = False Then Return nullable
Return DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc)
}
<Extension>
Public Shared Function Sanitize(Integer? nullable) AS Integer?
{
If nullable.HasValue = False Then Return nullable
If nullable.Value < 0 Then Return 0
Return nullable.Value
}
Somewhere in the code
Dim sanitizedDate As Date? = receivedDate.Sanitize()
Dim sanitizedAmount As Integer? = receivedAmount.Sanitize()
Extension methods have some downsides - for example you not able to "mock" them for unit testing, which force you to test "Sanitize" method every time it used (if you are using Test-First approach).

Return an inherited type from a method

Suppose I have the following classes defined:
Public Class BaseClass
...
End Class
Public Class DerivedClass
Inherits BaseClass
... Extra Fields, methods, etc ...
End Class
And then, in my code, I have a function with a signature of:
Public Function DoSomething(...) As List(Of BaseClass)
And when I try and return an object of type List(Of DerivedClass) from it, I get the error:
Value of type 'System.Collections.Generic.List(Of BaseClass)' cannot be converted to 'System.Collections.Generic.List(Of DerivedClass)'
I know not all the extra fields of the DerivedClass will be filled, but it would give me what I needed.
Is there a way to do this, or is this just considered bad programming practice? And, if so, what would be the right way to do this?
Have a look at this:
http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
Understanding Covariance and Contravariance will clear things up a bit :)
Covariance
Enables you to use a more specific type than originally specified.
You can assign an instance of IEnumerable (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable.
Example:
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Contravariance
Enables you to use a more generic (less derived) type than originally specified.
You can assign an instance of IEnumerable (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable.
Example:
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());
Invariance
Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant. You cannot assign an instance of IEnumerable (IEnumerable(Of Base) in Visual Basic) to a variable of type IEnumerable or vice versa.

Can properties of an object handle returned from a function be used without first assigning to a temporary variable?

I have a function that returns a handle to an instantiated object. Something like:
function handle = GetHandle()
handle = SomeHandleClass();
end
I would like to be able to use the return value like I would if I was writing a program in C:
foo = GetHandle().property;
However, I get an error from MATLAB when it tries to parse that:
??? Undefined variable "GetHandle" or class "GetHandle".
The only way I can get this to work without an error is to use a temporary variable as an intermediate step:
handle = GetHandle();
foo = handle.property;
Is there a simple and elegant solution to this, or is this simply impossible with MATLAB's syntax?
To define static properties, you can use the CONSTANT keyword (thanks, #Nzbuu)
Here's one example from MathWorks (with some errors fixed):
classdef NamedConst
properties (Constant)
R = pi/180;
D = 1/NamedConst.R;
AccCode = '0145968740001110202NPQ';
RN = rand(5);
end
end
Constant properties are accessed as className.propertyName, e.g. NamedConst.R. The values of the properties are set whenever the class is loaded for the first time (after the start of Matlab, or after clear classes). Thus, NamedConst.RN will remain constant throughout a session as long as you don't call clear classes.
Hmm, I don't like to disagree with Jonas and his 21.7k points, but I think you can do this using the hgsetget handle class instead of the normal handle class, and then using the get function.
function handle = GetHandle()
handle = employee();
end
classdef employee < hgsetget
properties
Name = ''
end
methods
function e = employee()
e.Name = 'Ghaul';
end
end
end
Then you can use the get function to get the property:
foo = get(GetHandle,'Name')
foo =
Ghaul
EDIT: It is not excactly like C, but pretty close.
The only way to have a static property in MATLAB is as a constant:
classdef someHandleClass < handle
properties (Constant)
myProperty = 3
end
end
then someHandleClass.myProperty will return 3.

Returning Generic type in VB.NET

Someone (w69rdy) in Stack Overflow helped me out with a great example to handle DB output, that could potentially be NULL, passed into a function. The problem is I can understand the method as written in C# but I am having a problem understanding how to rewrite the method in VB.NET. The method uses generics and I am lost. Here is the method written in C# ..
public T ParseValue<T>(System.Data.SqlClient.SqlDataReader reader, string column)
{
T result = default(T);
if (!reader.IsDBNull(reader.GetOrdinal(column)))
result = (T)reader.GetValue(reader.GetOrdinal(column));
return result;
}
How is this written in VB.NET? How does the method signature change when returning a generic type?
You can use the C# to VB.NET converter which produces the following results:
Public Function ParseValue(Of T)(reader As System.Data.SqlClient.SqlDataReader, column As String) As T
Dim result As T = Nothing
If Not reader.IsDBNull(reader.GetOrdinal(column)) Then
result = DirectCast(reader.GetValue(reader.GetOrdinal(column)), T)
End If
Return result
End Function
Additionally:
I would recommend the following resource to help know syntax differences between VB.NET and C#. It has a section on Generics:
VB.NET and C# Comparison
Public Function ParseValue(Of T)(reader As System.Data.SqlClient.SqlDataReader, _
column As String) As T
Dim result As T = Nothing
If Not reader.IsDBNull(reader.GetOrdinal(column)) Then
result = DirectCast(reader.GetValue(reader.GetOrdinal(column)), T)
End If
Return result
End Function
From C# to VB.NET converter.

What in the world does this code do? (C#)

I've been reading a book which is in C#. I'm a VB.NET developer (and a very junior one at that) and I'm having a lot of trouble with the following code that contains lots of things I've never seen before. I do have a basic knowledge of Lambda Expressions.
public List<T> SortByPropertyName(string propertyName, bool ascending)
{
var param = Expression.Parameter(typeof(T), "N");
var sortExpression = Expression.Lambda<Func<T, object>>
(Expression.Convert(Expression.Property(param, propertyName),
typeof(object)), param);
if (ascending)
{
return this.AsQueryable<T>().OrderBy<T, object>(sortExpression).ToList<T>();
}
else
{
return this.AsQueryable<T>().OrderByDescending<T, object>(sortExpression).ToList<T>
}
}
Could anybody illuminate me as to what this code is doing and what concepts are being used?
I am also trying to convert this code into VB.NET with little luck so any help there would be appreciated as well.
Overall, the code is sorting something (presumably a list?) by the specified property name in either ascending or descending order. There must already be a generic type T specified somewhere else in this class.
The code creates a new ParameterExpression by calling Expression.Parameter, then passes that parameter into the Expression.Lambda function, which creates a new lambda expression.
This expression is then used to sort the list by calling the OrderBy or OrderByDescending function (the choice depending on the ascending parameter) and returns the sorted list as a new List<T>.
I'm not in front of Visual Studio at the moment, but this should be a sufficiently close translation to VB for you.
Public Function SortByPropertyName(ByVal propertyName as String, ByVal ascending as Boolean) as List(Of T)
Dim param = Expression.Parameter(GetType(T), "N")
Dim sortExpression = Expression.Lambda(Of Func(Of T, Object))(Expression.Convert(Expression.Property(param, propertyName), GetType(Object)), param)
If ascending Then
return Me.AsQueryable(Of T).OrderBy(Of T, Object)(sortExpression).ToList()
Else
return Me.AsQueryable(Of T).OrderByDescending(Of T, Object)(sortExpression).ToList()
End If
End Function
This should work:
Return Me.AsQueryable.OrderBy(sortExpression).ToList
See also: http://www.codeproject.com/KB/recipes/Generic_Sorting.aspx