Cast generic structure to other structure - vb.net

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

Related

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.

How to overload a function when you only need to change a parameter data type

I have a function SelectNext() that takes a collection parameter (of type IEnumerable) and it selects the next item in the collection and return that item.
'BaseListTypes is an Enum
Function SelectNext(listType As BaseListTypes, lst As IEnumerable(Of Object)) As Object
Dim res As Object
'function body here....
Return res
End Function
The above function works great for any List(Of T) where T is an object or string.
However it fails when I pass it a List(Of My_STRUCTURE) (custom structure I have, which contains 3 string variables)
Obviously since Structure is like integer and other base types are a value types.
While objects are reference types. I can see why I am getting an error at runtime.
My question is, is there a better way than just overloading my function into something like:
Function SelectNext(listType As BaseListTypes, lst As IEnumerable(Of My_STRUCTURE)) As Object
You can go with generic impelementation like SelectNext<T>
class Program
{
Program()
{
//SelectNext_Old(new List<object>(0)); it works
//SelectNext_Old(new List<Point>(0)); id doesn't work
SelectNext(new List<object>(0));
SelectNext(new List<Point>(0));
}
public object SelectNext_Old(BaseListTypes listType, IEnumerable<object> lst)
{
return null;
}
public object SelectNext<T>(BaseListTypes listType, IEnumerable<T> lst)
{
return null;
}
}
I dont know VB but I guess you can get the idea!
In theory, this should work for any type, whether it be reference type or value type:
Function SelectNext(Of T)(listType As BaseListTypes, lst As IEnumerable(Of T)) As T
Dim res As T
'function body here....
Return res
End Function
I'd need more information to know for sure though. Your question is actually a bit vague. For one thing, what does "selects the next item in the collection" actually mean? You should edit your question to make it more complete, with a better description of what you're doing and what errors occur and where.

Create Data.SqlClient.SqlParameter with a SqlDataType, Size AND Value inline?

I have a function which can take a number of SqlParameter objects:
Public Shared Function RunSQL(sqlInput As String, Optional params As Data.SqlClient.SqlParameter() = Nothing) As String
The aim is basically being able to call the function and create the parameters if needed, as needed.
Unfortunately, there do not appear to be any constructors available that will allow me to specify the Value as well as the SqlDbType without having to add lots of additional parameters (that I do not need) as well.
The desired outcome would be something like:
Dim myStr As String = RunSQL("spMyStoredProc", {New Data.SqlClient.SqlParameter("#FieldName", Data.SqlDbType.NVarChar, 50, "MyValue")})
Obviously as the constructor for this does not exist, my question is basically to ask whether or not there is any way around this, as in any alternative, etc whilst still allowing the convenience of declaring the parameters in the function call?
I'd rather not have to declare all of my parameters beforehand just to set the Value property.
First, your code compiles but the last parameter is not the value but the source-column-name.
But you could use the With statement to assign the Value:
Dim myStr As String = RunSQL("spMyStoredProc",
{
New Data.SqlClient.SqlParameter("#FieldName", Data.SqlDbType.NVarChar, 50) With {.Value = "MyValue"}
})
See: Object Initializers

How to create a Dictionary with for value type that is known?

I want to create a Dictionary where the Key type is Integer and the Value type is the type of the class I am currently executing in.
I've tried the following:
Dim col as new Dictionary(Of Integer, Me.GetType())
but I am getting an error stating that `keyword does not name a type.
How do I create a dictionary based on the type of the executing class?
C# sample of creating dictionary of int to a type.
Methods used:
Type.MakeGenericType
Type.GetConstructor
ConstructorInfo.Invoke
The problem is mainly to express resulting type in somewhat type-safe manner. In Dictionary case one may resort to IDictionary, or continue to use reflection to manipulate objects.
It may also be possible to somehow express most manipulation with generic code which is invoked by more reflection with MakeGenericMethod
Sample:
var myType = typeof(Guid); // some type
// get type of future dictionary
Type generic = typeof(Dictionary<,>);
Type[] typeArgs = { typeof(int), myType };
var concrete = generic.MakeGenericType(typeArgs);
// get and call constructor
var constructor = concrete.GetConstructor(new Type[0]);
var dictionary = (IDictionary)constructor.Invoke(new object[0]);
// use non-generic version of interface to add items
dictionary.Add(5, new Guid());
Console.Write(dictionary[5]);
// trying to add item of wrong type will obviously fail
// dictionary.Add(6, "test");
Just use the class name Dim col as new Dictionary(Of Integer, MyClass)
On a side not using integer as your key can get confusing since a dictionary also uses integer for the index. If the keys are to be consecutive integers, you might be better served by a list.

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