Anonymous Type Collection - vb.net

How would you solve this? I want to return this collection:
Public Function GetShippingMethodsByCarrier(ByVal Carrier As ShippingCarrier) As List(of ?)
Return Carrier.ShippingMethods.Select(Function(x) New With {.ID = x.ID, .Name = String.Format("{0} {1}", Carrier.Name, x.Description)})
End Function
Thanks!!

You can't return an anonymous type from a function like this because it has no name.
Since this is a public function is should have a well defined return type. Create a new class holding those two properties.
Its possible to return it if the return type is an inferred generic parameter, but that's not what you want here. This is useful for LINQ where an anonymous type essentially gets passed through from a parameter to the result type, but not useful for what you're doing.
You could also use a Tuple, but then you'd lose the property names. And it wouldn't be extensible since adding a new property would break caller code. So I wouldn't recommend that either.

The problem here is you're attempting to return an anonymous type in a strongly typed manner. This is just not possible in VB.Net (or C# for that matter). Anonymous types are meant to be anonymous and their names cannot be stated explicitly in code. The two ways to work around this are to
Option #1 Use / Create a strongly named type like the following
Structure Item
Public ID as Integer
Public Name As String
Public Description As String
End Structure
Option #2 Set the return type to be Object and access the list in a late bound manner
EDIT
As CodeInChaos it is possible to return them in a strongly type manner in a generic context. But that doesn't appear to help you for this particular problem.

Related

How to expose the return type of a global class method?

I've written an ABAP Method, which returns me some analyses in a custom table.
Now I want to call this Method from an RFC module.
So far so good - the method works fine, but I'm curious of how to return this table?
Do I have to create a table / structure ... in SE11 to make it work because otherwise I can't refer to this table type or is there an easier way?
I'm quite new to ABAP so I don't know the best practices.
m_analyses = new zcl_manalyses( ).
data(lt_m_analyses) = m_analyses->analyse_m_data(
budat_from = budat_from
budat_to = budat_to
).
The TYPES statement can not only occur inside a method's body, but also inside the class definition, in which case it can be accessed from other places with class_name=>type_name (if it's public):
CLASS cl_user_registry DEFINITION PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF user,
firstname TYPE string,
lastname TYPE string,
END OF user,
users TYPE STANDARD TABLE OF user.
METHODS:
get_current_users
RETURNING users.
ENDCLASS.
DATA current_users TYPE cl_user_registry=>users.
current_users = cl_user_registry=>get_current_users( ).
You first have to create a structure in ABAP Dictionary (SE11), then you create a table type in SE11 as well.
You then reference the structure in the line type of the table type.
Try using the new global table type, it should work. (with typing 'TYPE')

How do I specify Enumerable.Count() instead of List.Count?

When attempting to use the Enumerable.Count() extension method from Visual Basic, the following code results in a compile-time error:
Imports System.Linq
Module Module1
Sub Main()
Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}
Dim i As Integer = l.Count(Function(foo) foo.Bar = "a")
Console.WriteLine(i)
Console.ReadLine()
End Sub
Class Foo
Sub New(ByVal bar As String)
Me.Bar = bar
End Sub
Public Property Bar As String
End Class
End Module
The error produced is:
'Public ReadOnly Property Count As Integer' has no parameters and its
return type cannot be indexed.
I'm targeting .NET 4.0, so extension methods should be supported. It's also worth noting that the equivalent code in C# infers the extension method correctly...
Why is the compiler unable to infer the use of Enumerable.Count, given the predicate I'm passing as an argument, and how can I use the extension method instead of the List's Count property?
The VB.Net compiler first tries to look up Count on the List instance, and it finds the Count property. This property is used instead of the extension method since fields and properties always shadow extension methods by name. I don't know where this is stated in the Visual Basic Language spec, but you can read more in this MSDN Magazin article:
Fields and properties always shadow extension methods by name. Figure 4 shows an extension method and public field with the same name and various calls. Though the extension method contains a second argument, the field shadows the extension method by name and all calls using this name result in accessing the field. The various overloaded calls will all compile, but their results at run time may be unexpected since they will bind to the property and use the default property behavior to return a single character or result in a runtime exception. It is important to choose the names of your extension methods so you avoid clashes with properties, fields, and existing instance methods.
So, Count(Function(foo) foo.Bar = "a") could mean: call the Count-property with Function(foo) foo.Bar = "a", or take the result of the Count-property and index it with Function(foo) foo.Bar = "a", which could be totally valid, since indexed properties in VB.Net can take any parameter.
This works in C# (I guess) because it is easier for the C# compiler to distinguish between method calls and a property access, because unlike VB.Net C# does not allow arbitrary parameters on properties and indexed properties.
To use the extension method, you call it like you would call every other static (shared) method:
Dim i As Integer = Enumerable.Count(l, Function(foo) foo.Bar = "a")
or call Call on IEnumerable explicitly:
Dim i As Integer = l.AsEnumerable().Count(Function(foo) foo.Bar = "a")
To answer your question as to why VB can't do what C# can in this case...
VB lets you access properties with () after the name, and also lets you call functions with no parameters by omitting the (). Also indexers use rounded brackets, instead of square brackets you have in C#. These are examples of tremendous VB features designed to make programming easier, which actually results in more ambiguous, harder to understand, and bug prone code.
So, in this particular case, VB sees you are accessing Count, and assumes the brackets after it are an indexer to the Count property, rather than arguments to the Count function.
C# sees the rounded brackets, and realises that you aren't accessing the indexer, you must be calling a function, so looks for a function.
Of course, there's room for ambiguity in C# as well. For example, a property with the same name as an extension method, which returns a delegate type will be called in preference to the extension method...
public Action Count { get; set; }
Ah... happy days.
As to how to call the IEnumerable.Count() function, a cast (preferably DirectCast()) or executing the extension method directly Enumerable.Count(...), is far far preferable to creating a whole new array to call count on...!
I'm not sure as to why you aren't getting the overload as an option, but you should be able to cast the list to IEnumerable(Of Foo) at which point the compiler will no longer allow List(Of Foo).Count property.
CType(l, IEnumerable(Of Foo)).Count(Function(foo) foo.Bar = "a")
If the list is converted to an array it works
Dim l As New List(Of Foo) From {New Foo("a"), New Foo("b"), New Foo("a")}
Dim i As Integer = l.ToArray.Count(Function(x) x.Bar = "a")

LINQ strong typing

I know LINQ is supposed to automatically return strongly typed result sets. When I attach an ObjectDataSource to a LINQ based method however (with no explicit return type), I don't get access to any of the columns defined in the LINQ.
Example method:
<System.ComponentModel.DataObjectMethod(ComponentModel.DataObjectMethodType.Select)> _
Public Function GetMarketClusterList() As IEnumerable(Of MarketCluster)
Return From d In db.tblMarkets
Select New MarketCluster With {.MarketCluster = d.MarketCluster}
Distinct
End Function
Public Class MarketCluster
Public MarketCluster As String
End Class
EDIT
I changed my code to use an explicit type and select into that. At least now I know the return type, but it doesn't help with the original problem. Even weirder, I found with the debugger that if I do
<%# Eval("MarketCluser") %> it fails and says "A field or property with the name MarketCluster was not found on the selected data source", but if I do <% Container.DataItem.MarketCluser %> it works fine!
Simply specify the return type:
Public Function GetMarketClusterList() As IEnumerable(Of MarketCluster)
Return From d In db.tblMarkets
Select d.MarketCluster
Distinct
End Function
(or use IQueryable instead of IEnumerable)
... assuming the type of d.MarketCluster is MarketCluster.
The return type is required if Option Strict is on anyway... which suggests that you've probably not got it on. I would strongly suggest that you enable Option Strict for your project whether you're using LINQ or not, unless you specifically need late binding etc (in which case I'd try to restrict it to only those places that need it).
The error message said the exact error, I just missed it. When it said could not find a "property", it meant class member called "Property". I changed my code to this and now it works perfectly.
Private _MarketCluster As String
Public Property MarketCluster As String
Get
Return _MarketCluster
End Get
Set(value As String)
_MarketCluster = value
End Set
End Property

What kind of array is Foo() as Foo()?

We generated a class from an XML file a while back. I think we used xsd.exe.
One of the main node collections in the XML file was rendered as:
<System.Xml.Serialization.XmlElementAttribute("PRODUCT")> _
Public Property PRODUCT() As PRODUCT()
Get
Return Me.pRODUCTField
End Get
Set
Me.pRODUCTField = value
End Set
End Property
And sure, there's PRODUCT class defined later on, and it worked fine. Serialized and deserialized fine. Didn't need to worry about it or manipulate it.
Only now we have to revisit and manipulate the data.
But what kind of collection (array?) is Public Property PRODUCT() As PRODUCT(), and how do we loop over it? And add to it?
Basic question, I know. Probably got too comfortable with generics and now xsd has thrown something at me which isn't List(of T) I'm running scared.
Don't be confused by the two sets of parens there. The first set, is simply the parens after the name of the property, whereas the second identifies the return type as an array of Product objects.
Similar to: Public Property IDs() As Integer()
That property returns only an array of integers, and the parens near IDs() only exist because you're declaring the property.
Since it appears to be a standard array of Product objects, you can loop over it with any number of normal loops:
For Each p As PRODUCT In obj.PRODUCTS()
...
Next
or
For i As Integer = 0 To obj.PRODUCTS.Length-1
...
Next i
Your code
Public Property PRODUCT() as PRODUCT()
Returns an array of Objects Of Type PRODUCT. Now whether that Type is a Collection, Structure, or Array I do not know with the code you have provided. The simplest way to loop over it would be as such.
For each prod as PRODUCT in rtnPRODUCTS
'Do Something
Next

Creating a function that uses a generic structure?

I am attempting to create a generic function that the students in my introductory VB .NET course can use to search a single dimension array of a structure.
My structure and array look like this:
Private Structure Survey
Dim idInteger As Integer
Dim membersInteger As Integer
Dim incomeInteger As Integer
Dim stateString As String
Dim belowPovertyLevelBoolean As Boolean
End Structure
Private incomeSurvey(199) As Survey
My generic function header looks like:
Private Function FindSurveyItem(Of xType As Structure)
(ByVal surveyIDInInt As Integer, ByVal surveyArrayIn() As xType) As Integer
??????
End Function
My call to the function looks like:
If FindSurveyItem(Of Survey)(CInt(idTextBox.Text), incomeSurvey) <> -1 Then
My question is: Is there a way to reference the individual structure fields in the array from inside the function? I was trying to make it generic so that the student could simply pass their array into the function - their structure may be named differently than mine and the field names may be different.
I suspect there are other ways to deal with this situation, but I was trying to keep it to just a simple single-dimension array of a structure. I don't think it is possible to do what I want, but I wondered what others thought.
Is there a way to reference the individual structure fields in the array from inside the function?
Generic instead of an array you need a collection type. Add LINQ Code:
Dim Surveys = From svys In xType
Where svys.idInteger = surveyIDInInt
Select svys
For Each rSurveys In svys
'''' Your Code
Next
This is rough answer fill in the details (I know imagine LINQ without SQL DB!!)
If you have a genric type parameter T you are only able to access members of instances of T that are known to exist at compile time. As every type derives from Object you have only the members of Object availiable - ToString(), GetType(), GetHashCode(), and Equals().
If you want to access other members you have to constrain what T is allowed to be. In your situation a interface would be the way to go. See MSDN for details.
You could also try to use reflection or check the actual type at runtime an perform a cast. The first is hard to impossible to do if you do not know much about the types you will get. And the later requires you to know possible types at compiletime and will not work in your situation, too.
Another way might be to pass a delegate to the search method that performs the actual comparison.
What you're looking for are predicates, if using ,net 3.5
dim arr() as structure
Array.Find(arr, function(item)(item.MyMember = MemberToMatch))
More combersome in earlier versions, see here for more info
The point being, that your function would look very like an implementation of Array.Find (if you didn't want to use the function provided), and the students would need to write their own predicate function.
No, there isn't. You can't know the type at compile time, therefore you cannot access members of that type. You would need change from a structure to a class that must implement IComparable so that you can use CompareTo between the item you pass in and the array you are passing in.
Though it's not entirely clear what you are trying to do within your method so I'm guessing by the name of the method.
You can use reflection to get those fields, but in this case that wouldn't have much meaning. How would you know that the passed type has the field you're looking for? There are other problems with that code as well.
Instead, to do this I would normally create an interface for something like this that had a public ID property, and constrain my input to the function to implement that interface, or as others mentioned use a built-in feature in the clr.
But that may be ahead of where your students are. If you just want an example of a generic function, my suggestion is to show them a type-safe implementation of the old vb imediate if function:
Public Function IIf(Of T)(ByVal Expression As Boolean, ByVal TruePart As T, ByVal FalsePart As T) AS T
If Expression Then Return TruePart Else Return FalsePart
End Function
Note that this is obsolete, too, as in VS2008 and beyond you can use the new If operator instead, which will work better with type inference and won't try to evaluate the expression that isn't returned.