We had the need to create the following object, basically a way to pass in integer or strings and fetch these back out as either an integer or a zero-padded string:
Public Class DOC_NUMBER
Private m_DOC_NUMBER As Integer = 0
Public Sub New(ByVal DOC_NUMBER As Integer)
m_DOC_NUMBER = DOC_NUMBER
End Sub
Public Sub New(ByVal DEPOT_CODE As String)
Dim ParseInput As Integer = 0
If Integer.TryParse(DEPOT_CODE, ParseInput) = False Then
m_DOC_NUMBER = 0
Else
m_DOC_NUMBER = ParseInput
End If
End Sub
Public Overrides Function ToString() As String
Return Right("0000000000" & m_DOC_NUMBER.ToString(), 10)
End Function
Public Function ToInteger() As Integer
Return m_DOC_NUMBER
End Function
End Class
An instance of this class can then be set as follows:
Dim DocumentID As New DOC_NUMBER("00123")
Dim DocumentID As New DOC_NUMBER(123)
This can then be assigned to other vars like this:
Dim NewDocumentID as Integer = DocumentID.ToInteger()
Dim NewDocumentID as String = DocumentID.ToString()
99% of the time however we'll be dealing with integers, so we'd like to make the .ToInteger() optional, for example:
Dim NewDocumentID as Integer = DocumentID
Obviously this currently gives the error "Value of type 'X.DOC_NUMBER' cannot be converted to 'Integer'"
How do we modify the class to automatically pass out an integer it's being assigned to an integer?
Examples in any .net language will be fine.
You can do what you want with implicit conversions. In C# that might look like this:
public static implicit operator string(DOC_NUMBER docNum)
{
// implement
}
public static implicit operator int(DOC_NUMBER docNum)
{
// implement
}
The VB.NET equivalent is the Widening Operator.
However, I'll point out that I find ToInteger() and ToString() (or as properties: AsInteger and AsString, perhaps) much clearer and simpler, and therefore better, than implicit conversions.
Related
I have a method that accepts Enum values as parameter, this answer is my reference. However I cant seem to make it work.
Enums
Public Enum FieldTypes
db_Alpha = 0
db_Memo = 1
db_Numeric = 2
End Enum
Public Enum SubTypes
st_None = 0
st_Phone = 35
st_Percentage = 37
st_Address = 63
st_Link = 66
End Enum
Method
Public Sub Foo(Of T)(ByVal Param1 As String, ByVal EnumParam As T)
Dim param As Type = GetType(T)
If param.IsEnum Then
Dim x = param.GetEnumValues()
End If
'Running some codes here
End Sub
Main
Public Sub Test()
Foo("TestName",FieldTypes.db_Memo)
End Sub
Im getting all the values of the type of Enum that was passed as a parameter.
I would want to reduce Method overloading and learn about generics here. Accepting C# and VB.net suggestions.
How could I get the value of of the Enum item passed?
How can I restrict this, that only the two types of Enum written is accepted?
Since an Enum is always a value type you can restrict your function "Foo" to only accept value types.
Public Sub Foo(Of T As Structure)(ByVal Param1 As String, ByVal EnumParam As T)
You could also restrict your generic function to only accept reference types
Public Sub Test(Of T As Class)(ByVal param As T)
or only accept types that implement a specific interface
Public Sub Test(Of T As IDispose)(ByVal param As T)
You can even concatenate those conditions
Public Sub Test(Of T As Class, IDispose, IEnumerable)(ByVal param As T)
Inside your function "Foo" you can check, that T is of the correct type like this:
If Not GetType(T).Equals(GetType(FieldTypes)) AndAlso _
Not GetType(T).Equals(GetType(SubTypes)) Then
Throw New Exception("Enum type not supported")
End If
To get the numeric value of "EnumParam" you can convert it to Integer or whatever else numeric type your enumeration is based on:
Dim x As Integer = Convert.ToInt32(EnumParam)
I have the following C# code which compiles correctly:
private string formatterCSharp(int number)
{
return "n" + number;
}
private void testInCSharp()
{
IEnumerable<int> list = new List<int>();
IEnumerable<string> formatted = list.Select(formatterCSharp);
}
As you can see, formatted should contain the contents of list, with formatterCSharp applied to each.
When I try to replicate this code in VB, I come up with this:
Private Function formatterVisualBasic(ByVal number As Integer) As String
Return "n" + number
End Function
Private Sub testInVB()
Dim list As IEnumerable(Of Integer) = New List(Of Integer)
Dim formatted As IEnumerable(Of String) = list.Select(formatterVisualBasic)
End Sub
However I get two compilation errors on the Select statement in VB.
BC30455
Argument not specified for parameter 'number' of 'Private Function formatterVisualBasic(number As Integer) As String'.
BC30518
Overload resolution failed because no accessible '[Select]' can be called with these arguments:
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Try
Dim formatted As IEnumerable(Of String) = list.Select(AddressOf formatterVisualBasic)
In VB.net you can't specify function name to pass it like that.
https://msdn.microsoft.com/en-us/library/y72ewk2b.aspx
Okay first off the select query is formed differently in VB.NET, here's how it should look:
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic(mynumber))
Secondly, if you want to get the same exception as in your C# code, you must add parentheses to the function call formatterVisualBasic, so that it is recognized as a function
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic())
(I am a ASP.NET programmer that uses mostly VB but can also program in C#)
I like to have a list(t) full with keywords and function names.
For example:
Dim MyList as new List(of MyFunctions)
MyList.add(new Myfunction("descr","DBGetDescription()"))
MyList.add(new Myfunction("Name","DBGetName()"))
MyList.add(new Myfunction("create","DBGetCreateTS()"))
Public Class Myfunctions
Public code as String
Public function as String
Public Sub new(in_code as String, in_function as String)
code = in_code
function = in_function
End Sub
End Class
'what do i want to search for:
Dim ToDo as String = "descr"
'search for it
Dim ThisFunction as MyFunction = MyList.find(function(x) x.code = ToDo)
'answer in 'thisfunction' gives me the name of the function i need to use
Dim FunctionIWantToUse = ThisFunction.function
(the DBGetDescription, DBGetName and DBGetCreateTS are existing routines)
I know the name of the function(routine) I want to start but it is inside a variable.
Is it possible in VB (or in C#) to start the function?
Is there a work around?
M.
You can define a delegate which is basically a reference to a function. This allows you to map "names" to actual functions, to make it possible to call a function from a string name.
Delegate Function MyDelegateType() as string
Sub Main
' Create a dictionary mapping string names to the corresponding function.'
' Ive used a dictionary in my sample code, as its more straightforward, '
' but it would be trivial to wrap the name/function tuple up in a class and use a list'
Dim MyList as new Dictionary(of string, MyDelegateType)
MyList.Add("descr", addressof DBGetDescription)
MyList.add("Name", addressof DBGetName)
MyList.add("create", addressof DBGetCreateTS)
' Get the function for a key, and call it.'
Console.WriteLine(MyList.Item("descr")())
End Sub
Function DBGetDescription() as string
return "MyDescription"
End Function
Function DBGetName() as string
return "MyName"
End Function
Function DBGetCreateTS() as string
return "MyCreateTS"
End Function
I'm not entirely sure that my wording is correct on the title above. I guess the easiest way to explain it is to put the code below for reference and then give what I'd like to have as the expected behavior.
Public Class Domino
Public ReadOnly MinimumSideValue As Integer = 0
Public ReadOnly MaximumSideValue As Integer = 6
Public Property key As Integer
Public Property Side1 As Integer
Public Property Side2 As Integer
Public Sub New(side1Value As Integer, side2Value As Integer)
Me.Side1 = side1Value
Me.Side2 = side2Value
' Key should be a two digit number. side values 5 and 1 -> 51
Me.key = Integer.Parse(Side1.ToString & Side2.ToString)
End Sub
Public Overrides Function ToString() As String
Return Convert.ToString(Me.key)
End Function
End Class
Public Class DominoCollection
Inherits System.Collections.CollectionBase
Public Sub AddDomino(newDomino As Domino)
' Not sure if I should be calling Contains on the Protected List object
If Not List.Contains(newDomino) Then
List.Add(newDomino)
End If
End Sub
Public Function Contains(objDomino as Domino) As Boolean
' Need to check if a domino that has the same key already exists
Dim found As Boolean = False
For i = 0 To List.Count - 1 'This will fail if the list is empty...
If DirectCast(List.Item(i), Domino).key = objDomino.key Then found = True
Next
Return found
End Function
End Class
The fist code listing is the class that will have multiple instances created. The second class is the collection that will contain instances of the first class. The collection should not contain duplicate items. So when I call the contains method on the built-in protected List object from the CollectionBase I'd like it to search the list of items looking for a duplicate with the same value in the key property of the first class.
I'm not entirely sure if I can override the List.Contains method because it's a protected object and/or if I should really be creating my own contains method. I tried creating my own .Contains method, but it fails if the list is empty.
EDIT
I know that my code isn't in C#. However since it's .NET, C# answers would help just as much as VB would.
EDIT: Revised Code
After seeing some of the solutions below I've got the following that so far is looking like it will work as I wanted. However I'm not sure I implemented the Item property correctly. I haven't tested it as of yet because I'm not to the point of being able to use it in the rest of the application, I'm just trying to get a framework down.
Friend Class DominoCollection
Private domCol As Dictionary(Of Integer, Domino)
Public ReadOnly Property Keys As System.Collections.Generic.Dictionary(Of Integer, Domino).KeyCollection
Get
Return domCol.Keys
End Get
End Property
Public ReadOnly Property Values As System.Collections.Generic.Dictionary(Of Integer, Domino).ValueCollection
Get
Return domCol.Values
End Get
End Property
Default Public Property Item(DominoKey As Integer) As Domino
Get
If domCol.ContainsKey(DominoKey) Then
Return domCol.Item(DominoKey)
Else
Throw New KeyNotFoundException(String.Format("Cannot find key {0} in internal collection"))
End If
End Get
Set(value As Domino)
If domCol.ContainsKey(DominoKey) Then
domCol.Item(DominoKey) = value
Else
Throw New KeyNotFoundException(String.Format("Cannot find key {0} in internal collection"))
End If
End Set
End Property
Default Public Property Item(domino As Domino) As Domino
Get
If domCol.ContainsKey(domino.key) Then
Return domCol.Item(domino.key)
Else
Throw New KeyNotFoundException(String.Format("Cannot find key {0} in internal collection"))
End If
End Get
Set(value As Domino)
If domCol.ContainsKey(domino.key) Then
domCol.Item(domino.key) = value
Else
Throw New KeyNotFoundException(String.Format("Cannot find key {0} in internal collection"))
End If
End Set
End Property
Public Sub New()
domCol = New Dictionary(Of Integer, Domino)
End Sub
Public Sub Add(dom As Domino)
If Not domCol.ContainsKey(dom.key) Then
domCol.Add(dom.key, dom)
End If
End Sub
Public Function Contains(dom As Domino) As Boolean
Return domCol.ContainsKey(dom.key)
End Function
' flexibility:
Public Function Remove(dom As Domino) As Boolean
If domCol.ContainsKey(dom.key) Then
domCol.Remove(dom.key)
Return True
Else
Return False
End If
End Function
Public Function Remove(key As Integer) As Boolean
If domCol.ContainsKey(key) Then
domCol.Remove(key)
Return True
Else
Return False
End If
End Function
Public Function Count() As Integer
Return domCol.Count
End Function
Public Sub Clear()
domCol.Clear()
End Sub
End Class
Given the central importance of Domino.Key, I think a class which uses a Dictionary would make things easiest. Since your code is always looking for that to decide if a Domino object exists etc, a Dictionary will help detect that and prevent dupes etc.
Friend Class DominoCollection
Private mcol As Dictionary(Of Integer, Domino)
Public Sub New()
mcol = New Dictionary(Of Integer, Domino)
End Sub
Public Sub Add(dom As Domino)
If mcol.ContainsKey(dom.key) = False Then
mcol.Add(dom.key, dom)
' optional: replace - dont know if this is needed
Else
mcol(dom.key) = dom
End If
End Sub
Public Function Contains(dom As Domino) As Boolean
Return mcol.ContainsKey(dom.key)
End Function
' flexibility:
Public Function Remove(dom As Domino) As Boolean
If mcol.ContainsKey(dom.key) Then
mcol.Remove(dom.key)
Return True
End If
Return False
End Function
Public Function Remove(key As Integer) As Boolean
If mcol.ContainsKey(key) Then
mcol.Remove(key)
Return True
End If
Return False
End Function
End Class
For an Item and Items:
Public Function Item(key As Integer) As Domino
Return mcol(key)
End Function
Public Function Items() As List(Of Domino)
Return New List(Of Domino)(mcol.Values)
End Function
Items hides the KeyValuePair typically required to iterate and allows you to do a simple For/Each loop on the collection (of needed):
Dim doms As New DominoCollection
doms.Add(New Domino(1, 2))
doms.Add(New Domino(2, 3))
doms.Add(New Domino(4, 6))
For Each d As Domino In doms.Items
Console.WriteLine("s1:{0} s2:{1} k:{2}", d.Side1, d.Side2, d.key.ToString)
Next
Output
s1:1 s2:2 k:12
s1:2 s2:3 k:23
s1:4 s2:6 k:46
Yes, you definitely should either inherit from a generic collection class or implement a generic collection interface.
I'm not sure on the exact syntax for VB, but I think instead of List.Contains you want to use the Linq extension Any with a lambda. I think it goes something like this:
List.Any(Function(d as Domino) d.key = newDomino.key)
This will return true if any of the elements inside List matches the key.
I'm trying to create a class thats holds two (or more) generic delegates as properties. This way I can pass the object to a method and use only one parameter on that method. The problem is that I get a warning to specify a type, but I don't know the type yet. The whole point is to defer type declaration until the object gets instantiated.
Here's some dummy code dummy code to show what I want to do.
Public Function Method1()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type string
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method2()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type Integer
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method3(container as Container)
//execute type specific code and the delegates
//Throw exception when type is not supported (yet)
end Function
public Class Container
Property Property1 as MyDel(of T)
Property Property2 as MyDel(of T)
end Class
Public delegate function Mydel(of T)()
That's it. The point of this being that when adding new information/functionality to the system it is easy to create a new method called Method4, which uses arguments as needed (and of course create the method that will be executed as delegate).
I would like to put two delegates in one class because their is a connection between them and things belonging together should be put together in one class; just like a Person class can hold a name and address.
This code doesn't work, because Container needs a Type T, which means Method3 needs a specific type, but i can't do that because then I can't call Method3 with different arguments as I showed. I tried using a wrapper class, but a type specification is needed all the time.
I know you can do this
Public Property TestProp() As [Delegate]
Get
End Get
Set(ByVal Value As [Delegate])
End Set
End Property
[source:http://www.xtremedotnettalk.com/showthread.php?t=96800]
But that is not generic. What am I missing?
BTW, this is an optimization for code calling Method3 with two arguments. That seemed to work fine. Then I thought trying to use only one argument, which would make the system easier to understand.
My old code looks something like this:
Public function OldMethod1()
Dim del1 as Mydel(of Integer) = AddressOf SomeMethod
Dim del2 as MyDel(of String) = AddressOf SomeOtherMethod
oldMethod3(del1, del2)
end Function
Public function OldMethod2()
Dim del1 as Mydel(of String) = AddressOf AnOtherMethod
Dim del2 as MyDel(of String) = AddressOf AgainSomeOtherMethod
oldMethod4(del1, del2)
end Function
Public Function oldMethod3(del1 as Mydel(of Integer), del2 as Mydel(of string))
//execute delegates
end Function
Public Function oldMethod4(del1 as Mydel(of string), del2 as Mydel(of string))
//execute delegates
end Function
When reading this code I saw that it is only the type of the parameter the determines the execution flow. So if you can determine the subtype of the generic you know what to do. That seemed to be possible (use typeOf and or GetType), so the next thing to do is to create a generic parameter object that could be a substitute for 'del1' (I now realize I took it even took one step further and created the Container class to hold both the parameters).
Any answer in C# or VB.net will do. I use both.
The question is a little confusing, so this may not be an answer, but it's too long for a comment! I think what you want to do is use Action(Of T) as the type for Container.Property1 and Container.Property2.
Something like this (this will run in LINQPad using Language: VB Program):
Sub Main
Dim c1 = New Container(Of String, String)()
c1.Property1 = AddressOf Method1
c1.Property2 = AddressOf Method1
Method3(c1)
Dim c2 = New Container(Of String, Integer)()
c2.Property1 = AddressOf Method1
c2.Property2 = AddressOf Method2
Method3(c2)
End Sub
Public Sub Method1(x As String)
Console.WriteLine("Method1: {0}", x)
End Sub
Public Sub Method2(x As Integer)
Console.WriteLine("Method2: {0}", x)
End Sub
Public Sub Method3(Of T1, T2)(container as Container(Of T1, T2))
Console.WriteLine("Method3 got {0}, {1}", GetType(T1), GetType(T2))
' Not sure how you would actually call the delegates
If GetType(T1) = GetType(String) Then
container.Property1.DynamicInvoke("Hello")
ElseIf GetType(T1) = GetType(Integer) Then
container.Property1.DynamicInvoke(123)
End If
If GetType(T2) = GetType(String) Then
container.Property2.DynamicInvoke("World")
ElseIf GetType(T2) = GetType(Integer) Then
container.Property2.DynamicInvoke(456)
End If
End Sub
Public Class Container(Of T1, T2)
Public Property Property1 As Action(Of T1)
Public Property Property2 As Action(Of T2)
End Class
This produces the following results:
Method3 got System.String, System.String
Method1: Hello
Method1: World
Method3 got System.String, System.Int32
Method1: Hello
Method2: 456