Get Type of Collection using Roslyn - vb.net

Using Roslyn for VB.Net I can get the Type of an Expression using the code below.
Dim ExpressionType As TypeInfo = SemanticModel.GetTypeInfo(ForEachStatement.Expression)
If the expression is a collection (List, Dictionary, Array, Collection...) how can I find out what is in the collection? For the example below I want to find DocumentIdAndRoot
Dim docs As List(Of DocumentIdAndRoot) = Await RemoveParameterAsync(document, parameter, root, cancellationToken)

If your question is specific to foreach, then you should use SemanticModel.GetForEachStatementInfo(), which returns a ForEachStatementInfo with all the necessary information.

What you could do is take the TypeInfo you get, and look at the ImplementedInterfaces property. One of those would be IEnumerable or ICollection, and from there you could look at what the generic parameter is.
If you do have a ForEach involved somewhere, you're still better off using Tamas' approach, since that will correctly implement the language rules there.

Related

Parameter.GetType() - Does the type has to be known at compilation time?

is something like this possible - and if so how?
Public Sub CreateGenericList(ByVal SampleObject As Object)
Dim genericList as new List(Of SampleObject.GetType())
End Function
I want to create a class that is able to deserialize a given XML-file.
The XML-file contains serialized values of a custom type, which is unknown at compilation time.
I thought it might be possible to just initialize the class with a parameter SampleObject and to then get that SampleObject's type for all further progressing.
But it seems as if the type for all operations has to be known at compilation time?
Is there a way around it or can you explain the problem to me?
The code example above is just to illustrate my problem
Thanks for the help,
Janis
Edit: Your answers might allready have solved the problem, I will read more on the topics "reflection" and "generics" and keep you up to date if i make any progress. So thanks allot for the help.
For those still interested:
I was asked for the purpose of my question and will try to explain it as best i can.
Public Function ReadAllObjects() As List(Of myObjectType)
Dim result As New List(Of myObjectType)
Dim ObjectSerializer As New System.Xml.Serialization.XmlSerializer(result.GetType)
Dim FileReader As New System.IO.FileStream(My.Settings.XMLPath, System.IO.FileMode.Open)
result = TryCast(ObjectSerializer.Deserialize(FileReader), List(Of myObjectType))
FileReader.Close()
RaiseEvent ReadingFinished()
Return result
End Function
This pretty much sums up what I want to create: A EasyXmlHandling.dll for further use, which will be initialized with the currently used variable type.
It is then supposed to be able to write and read from/to an XML-File in a really easy way, by just calling "ReadAllObjects" (returns a List of myObjectType) or "AddObject(ByVal theNewObject)"... (more functions)
I got all that to work with a custom class as type, so i could now easily re-use the EasyXmlHandling-code by just replacing that type in the sourcecode with whatever new class i will want to use.
I though would prefer to just call the .dll with a sample object (or the type of it) and to have it do everything else automaticly.
I hope that was understandable, but neither my english nor my technical vocabulary are really good ;)
So thanks again for the help and for reading through this.
I will try to get it to work with all your previous answers and will update the topic when i make further progress.
No, that is not possible (at least, not without using reflection). The whole point of specifying the type in a generic list, or any other generic type, is so that the compiler can perform compile-time type checking. If you aren't specifying the type at compile-time, there is no benefit to it at all. Beyond there being no benefit, it's also simply not supported. If you don't know the type at compile time, you should just use Object instead, since that will work with objects of any type, for instance:
Dim myList As New List(Of Object)()
If you need a list, however, which only allows one type of item, but that type is unknown at compile time, that is possible to do, but you would probably need to create your own non-generic list class for something like that. As far as I know, there is no non-generic list class provided in the framework which restricts its items to a single specified type.
Update
Now that you've explained your reason for doing this, it's clear that generics are your solution. For instance, you could implement it as as generic function, like this:
Public Function ReadAllObjects(Of T)() As List(Of T)
Dim result As New List(Of T)
Dim ObjectSerializer As New System.Xml.Serialization.XmlSerializer(result.GetType)
Dim FileReader As New System.IO.FileStream(My.Settings.XMLPath, System.IO.FileMode.Open)
result = TryCast(ObjectSerializer.Deserialize(FileReader), List(Of T))
FileReader.Close()
RaiseEvent ReadingFinished()
Return result
End Function
Then, you could call it, like this, passing it which ever type you want:
Dim cars As New List(Of Car) = ReadAllObjects(Of Car)()
Dim boats As New List(Of Boat) = ReadAllObjects(Of Boat)()
As you can see, that is the whole purpose of generics. They are a very powerful tool when you any to keep your code type-specific, but still be able to re-use it with different types. Reflection, on the other-hand is not a good fit in this particular situation. Reflection is also very useful, but should always be considered an option of last resort. If there is another way to do it, without reflection, that's usually the better way.

Is there any Count method for IEnumerable in VB.NET?

In C# if I have the following object:
IEnumerable<Product> products;
and if I want to get how many elements it contains I use:
int productCount = products.Count();
but it looks like there is no such method in VB.NET. Anybody knows how to achieve the same result in VB.NET?
Count is available in VB.NET:
Dim x As New List(Of String)
Dim count As Integer
x.Add("Item 1")
x.Add("Item 2")
count = x.Count
http://msdn.microsoft.com/en-us/library/bb535181.aspx#Y0
In later versions of .net, there is an extension method called Count() associated with IEnumerable<T>, which will use IList<T>.Count() or ICollection.Count() if the underlying enumerator supports either of those, or will iteratively count the items if it does not.
An important caveat not always considered with this: while an IEnumerable<DerivedType> may generally be substituted for an IEnumerable<BaseType>, a type which implements IList<DerivedType> but does not implement ICollection may be efficiently counted when used as an IEnumerable<DerivedType>, but not when cast as IEnumerable<BaseType> (even though the class would support an IList<DerivedType>.Count() method which would return the correct result, the system wouldn't look for that--it would look for IList<BaseType> instead, which would not be implemented.
In general, IEnumerable won't have a Count unless the underlying collection supports (eg List).
Think about what needs to happen for a generic IEnumerable to implement a Count method. Since the IEnumerable only executes when data is requested, in order to perform a Count, it needs to iterate through till the end keeping track of how many elements it has found.
Generally, this iteration will come to an end but you can setup a query that loops forever. Count is either very costly time-wise or dangerous with IEnumerable.

Standard Place for an Empty String Array in the JDK

Hi is there a standard place for accessing empty array constants in the JDK > 1.5.
When I want to do a conversion from a String Collection (e.g. ArrayList)to a String Array I find myself using my own
which is defined in my own Constants class:
public static final String[] EMPTY_STRING_ARRAY = new String[0];
And then in my client code something like:
String[] retVal = myStringList.toArray(Constants.EMPTY_STRING_ARRAY);
return retVal;
I was wondering if this is the "idiomatic" way of doing it or if I'm missing something
I get the impression from the brief search I did that this kind of thing is prevalent in many people's code.
Any ideas, answers, comment (aside from that I shouldn't really use String Arrays) greatly appreciated,
Cheers
Simon
I would recommend the following code improvement :
String[] retval = myStringList.toArray(new String[myStringList.size()]);
This way, the toArray method uses the provided, appropriately-sized array as a container, instead of creating a new one behind the scenes. From the ArrayList javadoc :
If the list fits in the specified
array, it is returned therein.
Otherwise, a new array is allocated
with the runtime type of the specified
array and the size of this list.
There are no array definitions like that in the JDK anywhere. The only two standard ways of doing it in the JDK are that which you list or
String ret[] = new String[myStringList.size()];
myStringList.toArray(ret);
return ret;

deserializing XML with dynamic types / converting string to System.Type

Hmmm I'm not sure if i titled this question properly or am asking it properly, but here goes.
I've got serialized objects (in XML) stored in a database, along with a string/varchar indicating the type.
Right now i am doing this: (because i have a finite number of different types)
Dim deserializer as XmlSerializer
If datatable("type") = "widget1" then
deserializer = new XmlSerializer(GetType(Widget1))
elseif datatable("type") = "widget2" then
deserializer = new XmlSerializer(GetType(Widget2))
...
i'd like to do something like
Dim deserializer as XmlSerializer
deserializer = new XmlSerializer(MagicallyConvertToSystemDotType(datatable("type"))
Am i barking up the wrong tree here?
Have you tried using Type.GetType? This takes a string parameter and returns a type for that name. You may have to give it additional information about the simple name "widget" and more along the lines of a full name. But it appears from your sample they should all have the same namespace so that shouldn't be a big hurdle.
The other option if you want an actual keyword Type to work with, and not a variable type is using something like (sorry I'm using C# and am too tired to do the VB conversion):
method in XmlSerializer like Deserialize(typestring, object);
method in XmlSerializer like Deserialize<T>(object);
public void Deserialize(string typestring, object obj)
{
MethodInfo deserialize = typeof(XmlSerializer)
.GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.Public)
.MakeGenericMethod(new Type[] { Type.GetType(typestring) });
deserialize.Invoke(this, new[] { obj });
}
Specifically, I think you're looking for this code here (NOTE: I don't work much in VB.Net, so I hope everything there is syntactically correct):
VB.Net:
// Get the type of object being deserialized.
Dim t as Type = Type.GetType(typeNameString);
// Make a new instance of the object.
Dim o as Object = Activator.CreateInstance(t);
C#:
// Get the type of object being deserialized.
Type t = Type.GetType(typeNameString);
// Make a new instance of the object.
object o = Activator.CreateInstance(t);
Edit (26 Oct, 2009, 15:10 GMT-0600): The Type.GetType(string typeNameString) method does not always recognize types as simply their fully qualified name. It would be in your best interest to be sure and include as much information as you can in your parameter string, as follows:
VB.Net/C#:
typeNameString = objectSerialized.GetType().Namespace + ", " + objectSerialized.GetType().Name + ", " + objectSerialized.GetType().Assembly.FullName
Less specifically, I just had the same problem, and after a lot of research, I finally came up with a nice solution for handling all most of this dynamically. I've posted the entire source code to a class capable of serializing and deserializing objects of any type not containing generics or arrays using Reflection. Feel free to take it and use it as your own. If anyone decides to add the handling for generics and arrays, please send me an updated copy so I can post it back on my blog (and you'll get an honorable mention ;-)...). It will serialize everything recursively, and has some special coding in there for enums as well.
Take a look and see if that covers everything you're looking for at:
http://maxaffinity.blogspot.com/2009/10/serialize-objects-manually.html
~md5sum~
Edit (27 Oct, 2009 14:38 GMT-0600): Corrected some misinformation about the class available from my blog.

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.