Implement Generic Interface via CodeDom - vb.net

The CodeDom is not generating legal VB for me when I try to implement a generic interface.
Here is my VB code to generate the VB code.
Private Sub RunTest()
Dim compileUnit = New CodeCompileUnit
Dim ns As New CodeNamespace()
compileUnit.Namespaces.Add(ns)
ns.Imports.Add(New CodeNamespaceImport("System"))
ns.Imports.Add(New CodeNamespaceImport("System.Collections.Generic"))
Dim fooCollection = New CodeTypeDeclaration("FooCollection")
ns.Types.Add(fooCollection)
fooCollection.TypeAttributes = Reflection.TypeAttributes.Public
fooCollection.IsClass = True
fooCollection.BaseTypes.Add(New CodeTypeReference(GetType(System.Object)))
fooCollection.BaseTypes.Add(New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
))
Dim method = New CodeMemberMethod
fooCollection.Members.Add(method)
method.Attributes = MemberAttributes.Private
method.Name = "GetEnumerator"
method.ReturnType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
method.PrivateImplementationType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
Dim provider = New Microsoft.VisualBasic.VBCodeProvider
Dim options = New Compiler.CodeGeneratorOptions
Dim writer = New IO.StringWriter
provider.GenerateCodeFromCompileUnit(compileUnit, writer, options)
Console.WriteLine(writer.ToString)
End Sub
And that will generate:
Public Class FooCollection
Inherits Object
Implements System.Collections.Generic.IEnumerable(Of Foo)
Function System_Collections_Generic_IEnumerable`1_GetEnumerator() _
As System.Collections.Generic.IEnumerable(Of Foo) _
Implements System.Collections.Generic.IEnumerable(Of Foo).GetEnumerator
End Function
End Class
The problem is the name of the function. The tick mark in the function name doesn't make for a legal function name.
It seems that when using the PrivateImplentationType property of the CodeMethodMethod the Name property gets used as the name of the method you are implementing, not the name of the function.
How do you explicitly set the function name or at least how do I get it to be something legal?

To get a compilable output, just use ImplementationTypes: I just changed one line of your code (but I include a simple line for reference, and the corrected ReturnType):
method.Name = "GetEnumerator"
method.ReturnType = New CodeTypeReference( _
"System.Collections.Generic.IEnumerator" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
)
method.ImplementationTypes.Add(New CodeTypeReference( _
"System.Collections.Generic.IEnumerable" _
, New CodeTypeReference() {New CodeTypeReference("Foo")} _
))
There doesn't seem to be a simple workaround using PrivateImplementationType. Microsoft's code in Microsoft.VisualBasic.VBCodeGenerator.GenerateMethod (which is in a Friend class so not easily overridden) caters for .'s, replacing them with _, but forgets to cater for ` in .NET 2.0-3.5 and (Of <type>) in 4.0. It is probably worth logging a bug at Connect, including pointing out the method name should be separately overridable compared to the method name on the interface being implemented.
You seem to need the functionality of PrivateImplementationType to support the non-generic GetEnumerator. Here is my simple adjustment of your original code to produce a single compilable library, with only warnings about empty code. This code still has "issues", such as the method.Attributes = MemberAttributes.Private being ignored where PrivateImplementationType is used...

Related

VB.NET linq extension methods such as ToList not working with generics

In a VB.NET class file, I'm trying to use extension methods such as ToList(), where the generic parameter is populated with what I know to be the subclass, in this context.
Dim specificOrders = request.Orders _
.Where(Function(x) x.GetType().Equals(GetType(SpecificOrderType))) _
.ToList(Of SpecificOrderType)()
However, I'm getting the error message "extension method is not generic or has no type parameters available". Any ideas as to why this is?
This method should be in the System.Linq namespace - I have it open and referenced in the file.
You don't use Where to filter by type. That's what the OfType method is for. It filters and casts:
Dim specificOrders = request.Orders.
OfType(Of SpecificOrderType)().
ToList()
In that case, OfType returns an IEnumerable(Of SpecificOrderType) and calling ToList on that returns a List(Of SpecificOrderType). That's how ToList works. It simply create a List(Of T) with the same generic type as the IEnumerable(Of T) that it's called on.
If you were going to use Where, you would use Cast to go from the base type to SpecificOrderType:
Dim specificOrders = request.Orders.
Where(Function(x) x.GetType().Equals(GetType(SpecificOrderType))).
Cast(Of SpecificOrderType)().
ToList()
One point to note about OfType is that it will match any item that can be cast as the specified type. That is usually what you want and probably the result that your original code would produce but it's worth noting that any item that was of a type that inherited SpecificOrderType would be excluded by your original code but included using OfType.
ToList() can not have type arguments, because that method is not generic.
So just use ToList()
Dim suborders = orders _
.Where(Function(x) x.GetType().Equals(GetType(nonspecificOrder))) _
.ToList()
Try below example.
'Order class
Public Class order
End Class
'specificOrder class
Public Class specificOrder
Inherits order
End Class
'nonspecificOrder class
Public Class nonspecificOrder
Inherits order
End Class
Usage:
Dim orders As List(Of order) = New List(Of [order])
Dim s1 As specificOrder = New specificOrder()
Dim s2 As specificOrder = New specificOrder()
Dim s3 As specificOrder = New specificOrder()
Dim s4 As specificOrder = New specificOrder()
Dim s5 As nonspecificOrder = New nonspecificOrder()
Dim s6 As nonspecificOrder = New nonspecificOrder()
orders.Add(DirectCast(s1, order))
orders.Add(DirectCast(s2, order))
orders.Add(DirectCast(s3, order))
orders.Add(DirectCast(s4, order))
orders.Add(DirectCast(s5, order))
orders.Add(DirectCast(s6, order))
Dim suborders = orders _
.Where(Function(x) x.GetType().Equals(GetType(nonspecificOrder))) _
.ToList()
Now the two "nonspecificOrder" type objects are returned.

How to assign linq anonymous type variable

I have a linq query that gives me an System.Collections.Generic.IEnumerable(of <Anonymous Type>). Actually I wanted to create a class to get better access to what I want form that query but I fail aready with declaring a member variable that should hold the query outcome. The query I use in the Sub New() of my class is (explained and c# version here:)
Dim jan1 = New DateTime(DateTime.Today.Year, 1, 1)
Dim startOfFirstWeek = jan1.AddDays(1 - CInt(jan1.DayOfWeek))
Dim weeks = Enumerable.Range(0, 54).[Select](Function(i) New With { _
Key .weekStart = startOfFirstWeek.AddDays(i * 7) _
}).TakeWhile(Function(x) x.weekStart.Year <= jan1.Year).[Select](Function(x) New With { _
x.weekStart, _
Key .weekFinish = x.weekStart.AddDays(4) _
}).SkipWhile(Function(x) x.weekFinish < jan1.AddDays(1)).[Select](Function(x, i) New With { _
x.weekStart, _
x.weekFinish, _
Key .weekNum = i + 1 _
})
My Class should look like:
Public Class WeekInfo
Private _weeks as ?????
end Class
Could anyone tell me what is the usual procedure to accomplish that task and how to put the find a type for my member to access the weeks variable?
VB.Net anonymous types are exposed as type Object so you're variable would be:
Dim weeks As IEnumerable(Of Object)
You will not get intellisense on the anonymous type's fields outside of the function you created them in but you can access them dynamically, i.e.
For Each week In weeks
Console.WriteLine(week.weekStart + " " + week.weekFinish)
Next
If you need to expose the type externally you should define a concrete class or struct instead.
Make your WeekInfo class look like this:
Public Class WeekInfo
Public Property WeekStart As Date
Public Property WeekEnd As Date
Public Property WeekNumber As Int32
End Class
And then load it with you linq query as list of your WeekInfo(s):
Dim jan1 = New DateTime(DateTime.Today.Year, 1, 1)
Dim startOfFirstWeek = jan1.AddDays(1 - CInt(jan1.DayOfWeek))
Dim weeks = Enumerable.Range(0, 54).Select(Function(i) New With { _
Key .weekStart = startOfFirstWeek.AddDays(i * 7) _
}).TakeWhile(Function(x) x.weekStart.Year <= jan1.Year).[Select](Function(x) New With { _
x.weekStart, _
Key .weekFinish = x.weekStart.AddDays(4) _
}).SkipWhile(Function(x) x.weekFinish < jan1.AddDays(1)).[Select](Function(x, i) New WeekInfo With { _
.WeekStart = x.weekStart, _
.WeekEnd = x.weekFinish, _
.WeekNumber = i + 1 _
}).ToList()

Getting a "Type Error" in vb.net code converted from C#

I have this code in VB.Net 2010 and I am getting a "type Error" on New().
This code was converted from C#.
What am I doing wrong?
Public Function CredentialGet(ByVal sKey As String, ByRef sCred As String)
Dim sCredential As Element.Credential
sCredential = apiclient.SearchCredentials(sSoftwareKey, SessionID,
New() {New Element.SearchTerm() With {.FilterKey = "APK", .Value = sKey}})
sCred = sCredential.CredentialID
End Function
New() what? You are missing an object name there. You're now passing in an anonymous object.
Remove the parentheses for the anonymous type. VB.Net does not use them in that context, but looks for the With keyword instead. And functions should return a value. You don't return anything, so use a Sub:
Public Sub CredentialGet(ByVal Key As String, ByRef Cred As String)
Dim Credential As Element.Credential
Credential = apiclient.SearchCredentials(sSoftwareKey, SessionID, _
New With {New Element.SearchTerm() With {.FilterKey = "APK", .Value = Key}})
Cred = Credential.CredentialID
End Sub
I also question this design. It would be better to return a string:
Public Function CredentialGet(ByVal Key As String) As String
Return apiclient.SearchCredentials(sSoftwareKey, SessionID, _
New With {New Element.SearchTerm() With {.FilterKey = "APK", .Value = Key}}).CredentialID
End Function

Using reflection I need to invoke a method with (of T) parameter

I'm trying to make this code with reflection since I want it to manage Technician and other types too.
m_Technician = m_Entities.CreateObject(Of Technician)() 'line#1
m_Technician.IDTechnician = Guid.NewGuid()
m_Entities.AddObject("Technicians", m_Technician)
I used this code with reflection to fill the entity and it work perfectly.
m_Entity = GetType(RFOPSEntities). _
GetMethod(FillMethodName).Invoke(m_Entities, New Object() {uniqueKey})
So I tried something like that for the line #1 :
m_Entity = GetType(RFOPSEntities). _
GetMethod("CreateObject"). _
Invoke(m_Entities, New Object({GetType("Technician")})
I think my difficulty is to pass the (Of Technician)
Thank you
You can use the MakeGenericMethod function to produce a generic MethodInfo from which you can invoke.
m_Entity = GetType(RFOPSEntities). _
GetMethod("CreateObject").MakeGenericMethod(GetType(Technician)). _
Invoke(m_Entities)

Linq-to-XML: Adding enumerations to objects

I'm trying to figure out how to add an enumeration to an object with Linq. For example:
Dim thingBlock = <Things>
<Thing Name="Ish">
<SmallThing>Jibber</SmallThing>
<SmallThing>Jabber</SmallThing>
</Thing>
<Thing Name="Ugly Guy">
<SmallThing Name="Carl"></SmallThing>
<SmallThing Marks="Marks"></SmallThing>
</Thing>
</Things>
Dim myList As New List(Of Thing)
myList.AddRange(thingBlock.<Thing>.Select(Function(th) New Thing With {.Name = th.#Name}))
Public Class Thing
Public Property Name As String
Public Property SmallThings As New List(Of String)
End Class
This works well to create new Thing and add them to myList, but I can't figure out how to add the IEnumerable of String to my SmallThings list. For example, this doesn't work:
myList.AddRange(thingBlock.<Thing>.Select(Function(th) New Thing With {.Name = th.#Name, th.Select(Function(st) .SmallThings.Add(st.Elements.#Name.ToString)}))
I just want to add all the <SmallThing>.#Name to SmallThings property of the Thing class.
I'm not sure which values you wanted to extract from the <SmallThing>s but this seems to work.
' adds "Jibber" and "Jabber" '
myList.AddRange(thingBlock.<Thing>.Select(Function(th) New Thing With _
{ _
.Name = th.#Name, _
.SmallThings = th...<SmallThing>.Select(Function(st) st.Value).ToList _
}))
' adds "Carl" '
myList.AddRange(thingBlock.<Thing>.Select(Function(th) New Thing With _
{ _
.Name = th.#Name, _
.SmallThings = th...<SmallThing>.Select(Function(st) st.#Name).ToList _
}))
The key was to convert the projection to a list since SmallThings expected a list (Select returns an IEnumerable(Of T))