Collection initialisation using iif() throws ArgumentNullException - vb.net

Can anyone tell me why this gives an error at run-time:
Dim mightBeNothing As List(Of String) = Nothing
Dim a As List(Of String) = IIf(mightBeNothing Is Nothing, New List(Of String)(), New List(Of String)(mightBeNothing))
I am getting ArgumentNullException on the second line. If I replace the last part with:
Dim a As List(Of String) = IIf(mightBeNothing Is Nothing, New List(Of String)(), New List(Of String)())
It works - but the constructor New List(Of String)(mightBeNothing) will never be called if mightBeNothing is nothing, so what is the issue?

the IIf function does not use short-circuit evaluation. So it will always evaluate everything, even if mightBeNothing is nothing.
MSDN on the subject.

First, collection initializers aren't supported prior to VB.NET 10.
Having said that, the first example is passing in a null (Nothing) value for the third argument. The IIf Function always evaluate all three arguments, regardless of the true/false state of the first argument. I believe that is why you are receiving the ArgumentNullException.
In the second case, none of the arguments are Nothing so it works, but doesn't give you the desired results.
I would recommend using an If Else:
Dim mightBeNothing As List(Of String) = Nothing
Dim a As List(Of String)
If mightBeNothing Is Nothing Then
a = New List(Of String)
Else
a = New List(Of String)
a.Add(mightBeNothing)
End If

Try using the IF operator instead of IIF. It will short-circuit. See this article on MSDN

Related

Clear List in Session Object

Im very new to Vb..
I get the Following error 'Option Strict On disallows late Binding' when I try to clear a list in my seesionObject like shown.
Private Sub ClearSessionList()
Dim context As Object = System.Web.HttpContext.Current.Session("MySessionobject")
context.MyListProperty = New List(Of String)
End Sub
The error persist when i try following casts aswell
DirectCast(context.MyListProperty, List(Of String))
CType(context.MyListProperty, List(Of String))
I assume my Casts are wrong in some way, anyone that can point out the error for me and show how I can clear the List?
Always use option strict, then you couldn't access MyListProperty until you haven't casted context from Object to it's actual type. Otherwise VB.NET will try to cast it for you, sometimes with weird results.
So presuming the type of it is MySessionObject:
Dim context As Object = System.Web.HttpContext.Current.Session("MySessionobject")
Dim myObj As MySessionObject = DirectCast(context, MySessionObject)
myObj.MyListProperty.Clear()

variable is referenced from scope, but not defined LINQ expression tree

I'm trying to get this LINQ expression:
Result = Result.Where(Function(Row) _WhereExpressions(0).InElements.Contains(Convert.ToString(Row(0))))
I have this code for it:
convertMethod = GetType(System.Convert).GetMethod("ToString", New Type() {GetType(Object)})
containsMethod = GetType(System.Collections.Generic.List(Of String)).GetMethod("Contains", New Type() {GetType(String)})
Dim listParameter = Expression.Parameter(GetType(List(Of String)), "_WhereExpressions(0).InElements")
expr = Expression.Call(whereMethod, Result.AsQueryable.Expression,
Expression.Lambda(Expression.Call(listParameter, containsMethod,
Expression.Call(convertMethod, Expression.ArrayAccess(rowParameter, Expression.Constant(index)))), rowParameter))
I get the desired expression, but if I compile, I get the error:
variable '_WhereExpressions(0).InElements' of type 'System.Collections.Generic.List`1[System.String]' referenced from scope '', but it is not defined
The _WhereExpressions(0).InElements is of course declared.
How can I fix it?
Thanks.
EDIT: here are all the declarations:
Dim whereMethod = GetType(Queryable).GetMethods(BindingFlags.Public Or BindingFlags.Static).First(Function(m) m.Name = "Where").MakeGenericMethod(GetType(Object()))
Dim convertMethod As MethodInfo = Nothing
Dim containsMethod As MethodInfo = Nothing
Dim rowParameter = Expression.Parameter(GetType(Object()), "Row")
The _WhereExpressions(0).InElements is a simple list of string, like this here:
Dim idlist As New List(Of String)
idlist.Add("1")
idlist.Add("2")
I read the linked post, but I can't really figure out, how I should solve my problem.
Expression trees have a lot capability, but looks a bit difficult for me.
EDIT2:
This is an example, what exactly I would like to achieve. Just copy and paste in vs:
Dim dt As New DataTable
dt.Columns.Add("f1", Type.GetType("System.String"))
dt.Columns.Add("f2", Type.GetType("System.Int32"))
For i = 0 To 100
dt.Rows.Add(i.ToString, i * 2)
Next
Dim indexes As New List(Of Integer)
indexes.Add(0)
indexes.Add(1)
Dim lst As New List(Of String)
lst.Add("10")
lst.Add("11")
Dim datarows As New List(Of DataRow)
For i = 0 To dt.Rows.Count - 1
datarows.Add(dt.Rows(i))
Next
Dim result As IEnumerable(Of Object())
result = datarows.Select(Function(row) indexes.Select(Function(index) row(index)).ToArray)
'I would like this as an expression:
result = result.Where(Function(row) lst.Contains(Convert.ToString(row(0))))
EDIT3: I got it:
Dim lst As Expression = Expression.Constant(list, GetType(System.Collections.Generic.List(Of String)))
It is hard to replicate code without knowing the full variables. I think your mistake though is in your understanding of Expression.Parameter. This is mainly used as a way to pass explicit parameters into the lambda: In your example, Row is a good example for when Expression.Parameter should be used. _WhereExpressions isn't an explicit parameter, it is a variable that I assume is in scope that you want to create a closure around.
You should also note that the second variable of the Expression.Parameter method is optional, and for debug purposes only: If you changed it to say: Expression.Parameter(GetType(List(Of String)), "Nonsense.nonsense"), you would probably see the error message change accordingly.
It sounds like you're trying to introduce a closure around _WhereExpressions. Using raw expression trees, this is fairly hard to do. The closest thing to an easy way to do this is to wrap Expression.Constant around _WhereExpressions.InElements. However, you're going to run into trouble if you're executing the compiled expression when _WhereExpressions is out of scope. See Issue with closure variable capture in c# expression.

Display the list of the Selected items in the checkboxlist

Dim list As New List(Of String)
list = chkparameter.Items
.Cast(Of ListItem)
.AsEnumerable()
.Where(Function(x) x.Selected)
.Select(Function(x) x.Value)
The error i am getting is
Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Web.UI.WebControls.ListItem,System.String]' to type 'System.Collections.Generic.List1[System.String]'.
How can i rectify it.
Thanks
If you want to assign the result to a List(Of String) variable then you need a List(Of String) object. You can all ToList on any enumerable list to create a List(Of T).
Also, your AsEnumerable call is pointless because Cast(Of T) already returns an IEnumerable(Of T).
Finally, declaring a variable on one line and then setting its value is so unnecessarily verbose. It's not wrong but it is pointless. In your case, not only are you declaring a variable but you're also creating an object that you never use. Don't create a New object if you don;t actually want a new object, which you don;t because you're getting an object on the very next line.
Dim list As List(Of String) = chkparameter.Items.
Cast(Of ListItem).
Where(Function(x) x.Selected).
Select(Function(x) x.Value).
ToList()
There's also no need to declare the type of the variable because it will be inferred from the initialising expression, i.e. ToList returns a List(Of String) so the type of the variable can be inferred from that. Not everyone likes to use type inference where it's not completely obvious though, so I'll let you off that one. I'd tend to do this though:
Dim list = chkparameter.Items.
Cast(Of ListItem).
Where(Function(x) x.Selected).
Select(Function(x) x.Value).
ToList()
By the way, notice how much easier the code is to read with some sensible formatting? If you're going to use chained function syntax like that, it's a very good idea to put each function on a different line once you get more than two or three.

Type of Generic seems worthless

Please see the code below:
Public Function ExecuteDynamicQuery(Of T As New)(ByVal sql As String, ByVal type As T) As List(Of T) Implements IGenie.ExecuteDynamicQuery
Dim iConnectionBLL As iConnectionBLL = New clsConnectionBLL
Dim paramValues() As DbParameter = New clsParameterValues().getParameterValues()
Using conn As DbConnection = iConnectionBLL.getDatabaseTypeByDescription("Genie2"), _
rdr As DbDataReader = clsDatabaseHelper.ExecuteReader(conn, CommandType.Text, sql, paramValues)
Dim list As List(Of T) = New List(Of T)
While rdr.Read()
Dim hello As New T
Dim method As MethodInfo = GetType(clsType).GetMethod("PopulateDataReader")
method.Invoke(hello, New Object() {rdr})
list.Add(hello)
End While
Return list
End Using
End Function
Is there a way of executing the SQL statement above without passing in type as an arguement. It seems a bit pointless - the only reason it is there is to let the function know the type of the generic.
Well you can change the method to not have the second parameter:
Public Function ExecuteDynamicQuery(Of T As New)(ByVal sql As String) As List(Of T) Implements IGenie.ExecuteDynamicQuery
However:
You'd need to change IGenie as well
The caller would then need to explicitly specify the type argument, instead of letting the compiler infer it on the basis of the argument (which would no longer be present)

VB.Net - how can i get the type of the object contained in an List

If I have a list...
dim l as List(of MyClass) = new List(of MyClass)
and I want to get the type of the objects contained in the list, how do I do that?
The obvious answer, that doesn't seem to be possible from my actual implementation, would be to do something like this...
public function GetType(byval AList as IList(of GenericType)) as System.Type
dim lResult as system.type = nothing
if AList.Count > 0 then lResult = AList(0).GetType
return lResult
end function
But what if the list is empty and I still want to know the type it contains?
There's a good article on this at MSDN, here
Basically you can use GetGenericArguments() to get an array of the types provided as arguments to your generic type. In the case of a List, there's only one argument so you will get what you need using eg
dim l as List(of MyClass) = new List(of MyClass)
dim t as Type = (l.GetGenericArguments())(0)