How to use CStr to convert generic parameter to String? - vb.net

I wrote a generic Sub and need to convert the input values to String to get them into a TextBox(in this case a powerpoint textbox). This looks like:
Sub InsertValueIntoTextbox(Of t)(ByVal sldNr As Integer, ByVal tbName As String, ByVal valueToInsert As T)
_pptSld = _pptPre.Slides(sldNr)
_pptSld.Shapes(tbName).TextFrame.TextRange.Text = CStr(valueToInsert)
End Sub
My problem is that it gives me this compiler-error:
The value of t cannot be converted into string
I tried a CStr (as you can see in the snippet) but this doesnt help too. Could anyone help me get this to work and can tell me why I cannot convert T to CStr?

Every Object can ToString, so why dont you use that?
Dim text = If(valueToInsert Is Nothing, "", valueToInsert.ToString())
_pptSld.Shapes(tbName).TextFrame.TextRange.Text = text
You cannot use CStr on generics. The type of the parameter must be convertible to String which is not guaranteed in your case. That's why you get the compiler error. I wouldn't use those old VB functions anymore. There are always .NET alternatives.

Related

VB.Net - Can you access the expected data type within a function?

I was wondering if there is any way to access the expected data type within a function similar to an event arg. I am doubtful that this is possible, though it would be an excellent feature.
I frequently work with (old and disorganized)Mysql databases creating interfaces through VB.Net. Often I will have an optional field which contains a NULL value in the database. I am frequently dealing with errors due to NULL and dbnull values in passing data to and from the database.
To complicate things, I often am dealing with unexpected datatypes. I might have an integer zero, a double zero, an empty string, or a string zero.
So I spend a fair amount of code checking that each entry is of the expected type and or converting NULLs to zeros or empty strings depending on the case. I have written a function ncc(null catch convert) to speed up this process.
Public Function ncc(obj As Object, tp As Type) As Object 'Null Catch Convert Function...
My function works great, but I have to manually set the type every time I call the function. It would be so much easier if it were possible to access the expected type of the expression. Here is an example of what I mean.
Dim table as datatable
adapter.fill(table)
dim strinfo as string
dim intinfo as long
strinfo = ncc(table.Rows(0).Item(0),gettype(String)) 'here a string is expected
intinfo = ncc(table.Rows(0).Item(0),gettype(Long)) 'here a long is expected
It would be so much more efficient if it were possible to access the expected type directly from the function.
Something like this would be great:
Public Function ncc(obj As Object, optional tp As Type = nothing) As Object
If tp Is Nothing Then tp = gettype(ncc.expectedtype)
That way I do not have to hard code the type on each line.
strinfo = ncc(table.Rows(0).Item(0))
You can make the ncc function generic to simplify calling it:
Public Function ncc(Of T)(obj As T) As T
If DbNull.Value.Equals(obj) Then Return Nothing
Return Obj
End Function
This kind of function will be able to in some cases infer the type, but if there's any possibility of null you'll still want to include a type name (because DBNull will be the inferred type for those values). The advantage is not needing to call gettype() and so gaining a small degree of type safety:
strinfo = ncc(Of String)(table.Rows(0).Item(0))
But I think this has a small chance to blow up at run time if your argument is not implicitly convertible to the desired type. What you should be doing is adding functions to accept a full row and return a composed type. These functions can exist as static/shared members of the target type:
Shared Function FromDataRow(IDataRow row) As MyObject
And you call it for each row like this:
Dim record As MyObject = MyObject.FromDataRow(table.Rows(i))
But, you problem still exists.
What happens if the column in the database row is null?
then you DO NOT get a data type!
Worse yet? Assume the data column is null, do you want to return null into that variable anyway?
Why not specify a value FOR WHEN its null.
You can use "gettype" on the passed value, but if the data base column is null, then you can't determine the type, and you right back to having to type out the type you want as the 2nd parameter.
You could however, adopt a nz() function (like in VBA/Access).
So, this might be better:
Public Function ncc(obj As Object, Optional nullv As Object = Nothing) As Object
If obj Is Nothing OrElse IsDBNull(obj) Then
Return nullv
End If
Return obj
End Function
So, I don't care if the database column is null, or a number, for such numbers, I want 0.
So
dim MyInt as integer
Dim MyDouble As Double
MyInt = ncc(rstData.Rows(0).Item("ContactID"), 0)
MyDouble = ncc(rstData.Rows(0).Item("ContactID"), 0)
dim strAddress as string = ""
strAddress = ncc(rstData.Rows(0).Item("Address"), "")
Since in NEAR ALL cases, you need to deal with the null from the DB, then above not only works for all data types, but also gets you on the fly conversion.
I mean, you CAN declare variables such as integer to allow null values.
eg:
dim myIntValue as integer?
But, I not sure above would create more problems than it solves.
So,
You can't get exactly what you want, because a function never has knowledge of how it's going to be used. It's not guaranteed that it will be on the right-hand side of an assignment statement.
If you want to have knowledge of both sides, you either need to be assigning to a custom type (so that you can overload the assignment operator) or you need to use a Sub instead of an assignment.
You could do something like this (untested):
Public Sub Assign(Of T)(ByVal field As Object, ByRef destination As T,
Optional ByVal nullDefault As T = Nothing)
If TypeOf field Is DBNull Then
destination = nullDefault
Else
destination = CType(field, T)
End If
End Sub
I haven't tested this, so I'm not completely certain that the compiler would allow the conversion, but I think it would because field is type Object. Note that this would yield a runtime error if field is not convertible to T.
You could even consider putting on a constraint requiring T to be a value type, though I don't think that would be likely to work because you probably need to handling String which is a reference type (even though it basically acts like a value type).
Because the destination is an argument, you wouldn't ever need to specify the generic type argument, it would be inferred.

VB.NET Argument Prompt cannot be converted to type string

I'm made a new list of string and when I try to add something there it gives me an error: Argument 'Prompt' cannot be converted to type 'string
My code:
Dim variables As New List(Of String)
Try
variables.Append(CStr(TextBox1.Text))
variables.Append(CStr(TextBox2.Text))
MsgBox(variables)
Catch ex As Exception
MsgBox(ex.Message)
End Try
How can I fix that?
The error is coming from your use of MsgBox(). You're passing it the variables variable, and it doesn't know how to convert a List(Of String) to a String.
As stated by jmcilhinney, you should be using Add() instead of Append().
Additionally, you should use MessageBox.Show() instead of MsgBox().
As for the error, we can only assume you want to see all the current values in your List? If so, one solution is to use String.Join() and display that instead:
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
MessageBox.Show(String.Join(",", variables))
But your variables list should be declared at Form level so that you aren't creating a new one each time. It isn't clear from your post if this is the case or not.
You should be calling the Add instance method of your List(Of String). Append is an extension method and is not appropriate in that scenario. Append would be used for an enumerable list that you wanted to enumerate on the spot, e.g.
Dim names = {"Peter", "Paul", "Mary"}
Dim pNames = names.Where(Function(name) name.StartsWith("P"))
For Each pName In pNames.Append("Philip")
Console.WriteLine(pName)
Next
In that case the Append only affects the list being enumerated by the loop where it's used. The original list is unaffected.
You should be using the Add method:
Dim variables As List(Of String) = New List(Of String)
variables.Add(TextBox1.Text)
(No need for the redundant CStr as TextBox1.Text is already of type String.)
As #Mary will no doubt suggest - rightly! - always have Option Explicit and Option Strict set to On (and in my opinion Option Infer Off); it'll help you with any syntax issues.
And always pay attention to the syntax/compiler suggestions in the left margin which will give you clues/tips to fix or improve your code.
Lastly, refer to the Microsoft documentation if in doubt. It should be your first port-of-call if you're unsure of anything.
As several people have mentioned, use the .Add method not .Append.
The .Text property of a TextBox is already a String. No need to convert with CStr().
A message box displays a String. variables is not a String; it is a List(Of String). To see what is in your list use a For Each loop.
Private Sub OpCode()
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
For Each s In variables
MsgBox(s)
Next
End Sub
#SteveCinq is correct :-); turn on Option Strict.

How to specify a lamdba function in VB.NET?

I am trying to understand the usage of lambda functions in VB.NET, using the following example. But something is not correct. Specifying 'Dim t as string = ...' doesn't work either. Can this be done?
Dim tagsList As New Dictionary(Of String, String)
Dim Name as string = "abc"
Dim t = Function(aName As String) If(tagsList.ContainsKey(aName), tagsList(aName), Nothing)
Dim Tag as string = t(Name)
Error BC30209 Option Strict On requires all variable declarations to have an 'As' clause.
Error BC30574 Option Strict On disallows late binding.
Based on the errors, it appears that you have Option Infer Off.
I'd recommend to turn Option Infer On, then your code will work exactly as presented. I can't really think of a good reason to ever have inference off.
Without type inference, you must declare the type of t. As noted in the comments, you can declare t to be [Delegate], but then you'll also have a problem at the point where you try to call. In order to get it to compile, I needed to change the right hand side to Cstr(t.DynamicInvoke(name)). A better alternative, if you're going this route, is to declare the delegate type explicitly (which must be done at class/module level), e.g.
Delegate Function RetrieveTag(ByVal name As String) As String
Then you declare t as having this type, e.g.
Dim t As RetrieveTag = ...

SQL Data Type function parameter when writing function to create a stored procedure parameter

I'm currently writing a function that will make it easier to add parameters to stored procedures programmatically, but so far the only stumbling block I'm having is adding the data type in as a function parameter. The code is:
Public Shared Function spParamAdd(cmd As SqlCommand, paramName As String, dataType As SqlDbType, value As String)
Dim command = cmd.Parameters.Add(paramName, SqlDbType.dataType).Value = value
Return command
End Function
And the code I'm trying to derive this from is:
cmd.Parameters.Add("#USERID", SqlDbType.Int).Value = 1
Finally, the error I'm getting is:
dataType is not a member of System.Data.SqlDbType
Any suggestions welcome.
Sorry it took some time, here's a great solution for you... This code is tried and tested
This will add parameter's to your command object easily if your looking for a way to add them...
Method to add parameters to a command object
As you can see below, we only need the command object (ByRef) so we can directly use it, your parameter name itself, the data type and finally the value. Also there's a select case as we need to know exactly what data type we are dealing with so we can cast our value to the correct type...
Public Shared Sub spParamAdd(ByRef cmd As SqlCommand, ByVal paramName As String, ByVal dataType As SqlDbType, ByVal value As Object)
'Need to make sure we have the correct datatype, add more as needed!
Select Case dataType
Case SqlDbType.VarChar
cmd.Parameters.AddWithValue(paramName, CStr(value)).DbType = dataType
Case SqlDbType.Int
cmd.Parameters.AddWithValue(paramName, CInt(value)).DbType = dataType
End Select
End Sub
Example Useage
Dim cmd As New SqlCommand
spParamAdd(cmd, "#Customer_ID", SqlDbType.Int, 12)
spParamAdd(cmd, "#Last_Name", SqlDbType.VarChar, "Dickins")
Another Example - Not really suggested...
You can also omit the select case BUT you better make sure your value you send in and the data type is the same!
Public Shared Sub spParamAdd(ByRef cmd As SqlCommand, ByVal paramName As String, ByVal dataType As SqlDbType, ByVal value As Object)
cmd.Parameters.AddWithValue(paramName, value).DbType = dataType
End Sub
Let me know how this works out for you!
Happy Coding!

Looping Through Boolean Parameters

I have a generic question to see if anyone can help me with a better solution.
I have a .NET method that takes in 20+ boolean values, passed in individually.
For each parameter that is true I need to add a value to list.
Is there a more efficient way to add the values to the list besides have an if statement for each boolean?
Example:
Public Function Example(ByVal pblnBool1 as boolean, _
ByVal pblnBool2 as boolean, _
ByVal pblnBool3 as boolean)
If pblnBool1 then
list += "A"
End If
If pblnBool2 then
list += "B"
End If
End Function
Obviously this code isn't correct but it shows what I'm trying to do.
Anyone have any ideas?
Thanks
First off, having 20+ params sucks.
Secondly, you can use the ParamArray keyword to declare that you want the values passed to you in an array. (I don't think this is CLS compliant, meaning some languages won't be able to call your function without bundling the values into an array. But VB and C# can both easily work with each other's param arrays.) If you don't want to do that, you can always create the array yourself in your function. But i'd rather let the language and/or framework do that for me.
This is not optimized or anything; it's just an example.
sub Example(paramarray bools() as Boolean)
static vals() as String = {"A", "B", "C"}
if bools.Length > vals.Length then
throw new ArgumentException(String.Format( _
"Too many params! ({0} max, {1} passed)", _
vals.Length, bools.Length _
))
end if
for i as Integer = 0 to bools.Length - 1
if bools(i) then list += vals(i)
next
end sub
I assume that list is some member variable, since it's not defined in your code. If you intend for it to be the return value, then declare it in the function, and return it at the end. (And of course, turn sub Example(...) and end sub into function Example(...) as String and end function).
If you have 32 or fewer booleans to pass, you can use an instance of BitVector32. This allows to pass them all in a single integer. It provides methods for setting and retrieving the values.
Just make an array of boolean values and do a for...each loop through it. Or if you need to be selective, a 2-D array.