How do I determine if a value is an instance of a generic type, ignoring the type parameter, in vb.net? - vb.net

I have a class C(Of T). I want to determine if some given value has type C, regardless of what T is. For example, I might want to determine if a value is a strongly-typed list, regardless what type of items the list stores.
I just need to know how to do it in VB.net. In Java the syntax is like this:
var result = obj instanceof Gen2<?>;

I believe a compact solution for your problem would be:
Dim result = (obj.GetType().GetGenericTypeDefinition().Equals(GetType(Gen2(Of ))))
Explanation:
Gets the Type object representing the base type of instance obj
Gets the generic type underlying the compiler instance type.
Gets the generic type of Gen2 without a qualifying parameter.
Compares the two generics to see if they are equal and returns the result.
It's not nearly as compact as the Java solution you posted (unless I'm mistaken, C# doesn't support either the instanceof keyword or the Java generic wildcard syntax), but it will work.
Edit: Prompted by Cory Larson's comment below, I should add that while the method I posted only works for directly comparing the generic to a known generic type, if you want to find out if it implements a generic interface, use:
Dim result = (obj.GetType().GetGenericTypeDefinition().GetInterface(GetType(IMyGeneric(Of )).FullName) IsNot Nothing)

Sure, sort of. For example:
Dim obj As IList(Of Double) = New List(Of Double)
Dim result As Boolean = obj.GetType().IsGenericType AndAlso _
obj.GetType().GetGenericTypeDefinition().Equals(GetType(IList(Of )))
For that, the result is False. If you change the comparison from IList(Of ) to just List(Of ), then it works.
Dim obj As IList(Of Double) = New List(Of Double)
Dim result As Boolean = obj.GetType().IsGenericType AndAlso _
obj.GetType().GetGenericTypeDefinition().Equals(GetType(List(Of )))
Will return True.
EDIT: Dang, Dan Story got it first.

If you are seeking to find out whether a type is a Foo(Of T) because you're interested in using some property which does not depend upon T, I would suggest that you should make that property available in either a non-generic base class or a non-generic interface. For example, if defining an ISuperCollection(Of T) which provides array-like access, one could offer a non-generic ISuperCollection collection which implements methods Count, RemoveAt, CompareAt, SwapAt, and RotateAt (calling RotateAt(4,3,1) would rotate three items, starting at item 4, up one spot, thus replacing item 5 with 4, 6 with 5, and 4 with the old value of 6), and have ISuperCollection(Of T) inherit from that.
BTW, if you segregate reader interfaces from writer interfaces, the reader interfaces can be covariant and the writer interfaces contravariant. If any property or indexer implements both read- and write- functions, you'll need to define a read-write interface which includes read-write implementations of any such property or indexer; a slight nuisance, but IMHO worth the small additional effort.

Related

Identical objects in a list produce different hashes and fail comparison tests

I have a weird issue. I want to implement an extension to List with a function to merge another list into it excluding the duplicate values:
<Extension()>
Public Sub AddUnique(Of T)(ByVal self As IList(Of T), ByVal items As IEnumerable(Of T))
For Each item In items
If Not self.Contains(item) Then self.Add(item)
Next
End Sub
Now, I have a class that I'll be creating objects from, and adding them to a list:
Class DocInfo
Public Property title As String
Public Property fullPath As String
Sub New(title As String, fullPath As String)
Me.title = title
Me.fullPath = fullPath
End Sub
End Class
Then, I have a list as a global variable:
Public docsInfo As New List(Of DocInfo)
And then I have a button handler that adds new items to that list:
Private Sub AddToList_Button_Click(sender As Object, e As RoutedEventArgs)
Dim candidateItems As New List(Of DocInfo)
For Each doc In selectedDocs
candidateItems.Add(New DocInfo(doc.GetTitle(), doc.GetPathName()))
Next
docsInfo.AddUnique(candidateItems)
End Sub
(The doc and selectedDocs variables are outside of the scope of this question.)
Now, the important bit - GetTitle() and GetPathName() return the same strings on every button click (I have the same docs selected between clicks). Meaning that DocInfo objects that are added to the candidateItems, and then added to docsInfo, are identical. Nevertheless, the extension function AddUnique fails, resulting in duplicates in the list.
Puzzled, I ran GetHashCode() on these duplicate DocsInfo class objects:
For Each docInfo In docsInfo
Console.WriteLine(docInfo.title)
Console.WriteLine(docInfo.fullPath)
Console.WriteLine(docInfo.GetHashCode())
Next
And this is the output:
Assem1^Test assembly.SLDASM
C:\Users\Justinas\AppData\Local\Temp\swx5396\VC~~\Test assembly\Assem1^Test assembly.SLDASM
7759225
Assem1^Test assembly.SLDASM
C:\Users\Justinas\AppData\Local\Temp\swx5396\VC~~\Test assembly\Assem1^Test assembly.SLDASM
14797678
With each button click, I am getting identical DocsInfo objects (title and fullPath properties have the same values), yet their hashes are different every time, and every comparison I can think of, fails to acknowledge that these objects are for all intents and purposes idendical.
Why is this happening? And how can I fix the AddUnique extension function to work as intended?
This behavior is because of the difference in .NET between "Reference" types and "Value" types. The fundamental philosophy of these is that for "Reference" types, object identity takes precedence over contents (that is, two different object instances with the same contents are still considered distinct), while for "Value" types, the contents are the only thing that matters.
In VB, Class denotes a reference type while Structure denotes a value type. Their respective behaviors are what you would expect, then: by default, Equals on a Class is equivalent to ReferenceEquals, checking to see if the references are the same, and GetHashCode returns a value based on the object identity. Equals on a Structure does member-wise value equality, and GetHashCode returns a value based on the hash codes of the members.
There are a couple of different options for overriding the default behavior, with differing impacts and levels of intrusiveness.
You can change Class to Structure. If you do so, I would strongly recommend to eliminate any mutable behavior on them (i.e. make all fields and properties ReadOnly), because mutable Structures can be extremely hard to reason about correctly. If you really do have immutable data, though, this is the easiest to maintain because .NET will already do what you want, you don't have to maintain your own Equals or GetHashCode override.
You can override GetHashCode and Equals on your Class to act like the Structure versions. This won't change anything else about your class, but it will make it act like a value type for the purposes of containers and sequences. If you're worried about maintenance, an alternative would be to do something reflection-based, though this shouldn't be used for anything that will be high-throughput because reflection is generally not particularly performant.
I believe the hashing and ordering containers take optional constructor parameters that will let you provide a class for overriding the behavior of the contents without altering the Class itself. You could do something like this. I'd recommend to look at the MSDN docs for HashSet.

Understanding Array.ConvertAll, can I DirectCast?

I have a base class, DtaRow, that has an internal array of Strings containing data. I have dozens of subclasses of DtaRow, like UnitRow and AccountRow, who's only purpose is to provide Properties to retrieve the values, so you can do aUnit.Name instead of aUnit.pFields(3).
I also have a DtaTable object that contains a Friend pRows As New Dictionary(Of Integer, DtaRow). I don't generally insert DtaRows into the DtaTable, I insert the subclasses like UnitRows and AccountRows. Any given table has only one type in it.
Over in the main part of the app I have an accessor:
Public Readonly Property Units() As IEnumerable
Get
Return Tables(5).pRows.Values 'oh oh oh oh table 5, table 5...
End Get
End Property
This, obviously, returns a list of DtaRows, not UnitRows, which means I can't do MyDB.Units(5).Name, which is the ultimate goal.
The obvious solution is to Dim ret As New UnitRow() and DirectCast everything into it, but then I'm building thousands of new arrays all the time. Uggg. Alternately I could put DirectCast everywhere I pull out a value, also uggg.
I see there is a method called Array.ConvertAll that looks like it might be what I want. But maybe that just does the loop for me and doesn't really save anything? And if this is what I want, I don't really understand how to use DirectCast in it.
Hopefully I'm just missing some other bit of API that does what I want, but failing that, what's the best solution here? I suspect I need...
to make a widening conversion in each DtaRow subclass?
or something in DtaTable that does the same?
You can use ConvertAll to convert an array into a different type.
Dim arr(2) As A
Dim arr2() As B
arr(0) = New B
arr(1) = New B
arr(2) = New B
arr2 = Array.ConvertAll(arr, Function(o) DirectCast(o, B))
Class A
End Class
Class B
Inherits A
End Class
In your case, I think it would look like this
Return Array.ConvertAll(Tables(5).pRows.Values, Function(o) DirectCast(o, UnitRow))
Note that this will create a new array each time.
You can cast the objects into a list(Of String) based on the field you want.
Return Tables(5).pRows.Values.Cast(Of DtaRow).Select(Function(r) r.name).ToList
YES! I went non-linear. This only works because of OOP...
My ultimate goal was to return objects from the collection as a particular type, because I knew I put that type in there in the first place. Sure, I could get the value out of the collection and CType it, but that's fugly - although in C# I would have been perfectly happy because the syntax is nicer.
So wait... the method that retrieves the row from the collection is in the collection class, not the various subclasses of DtaRow. So here is what I did...
Public ReadOnly Property Units() As IEnumerable
Get
Return Tables(dbTblUnits).pRow.Values
End Get
End Property
Public ReadOnly Property Units(ByVal K as Integer) As UnitRow
Get
Return DirectCast(Tables(dbTblUnits)(K), UnitRow)
End Get
End Property
Public ReadOnly Property Units(ByVal K as String) As UnitRow
Get
Return DirectCast(Tables(dbTblUnits).Rows(K), UnitRow)
End Get
End Property
Why does this solve the problem? Well normally if one does...
Dim U as UnitRow = MyDB.Units(K)
It would call the first method (which is all I had originally) which would return the .Values from the Dictionary, and then the Default Property would be called to return .Item(K). But because of the way the method dispatcher works, if I provide a more specific version that more closely matches the parameters, it will call that. So I provide overrides that are peers to the subclasses that do the cast.
Now this isn't perfect, because if I just call Units to get the entire list, when I pull rows out of it I'll still have to cast them. But people expect that, so this is perfectly acceptable in this case. Better yet, when I open this DLL in VBA, only the first of these methods is visible, which returns the entire collection, which means that Units(k) will call the Default Property on the DtaTable, returning a DtaRow, but that's fine in VBA.
OOP to the rescue!

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.

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")

cast to generic type at runtime

Ok, i don't think this is possible so I thought I would ask to make sure. I'm in the process of creating a function which reflects over the properties in a class and adds them to this data structure I have. Some of the properties are generic types.
So say we have DataType(Of T) that has a .Value property of type T:
Dim properties = GetType(MyType).GetFields(Reflection.BindingFlags.Public Or _
Reflection.BindingFlags.Instance)
For Each prop As fieldinfo In properties
Collection.Add(prop.Name,prop.GetValue(poco))
Next
In the collection.Add for primitive types (Integer, String, etc.…) I just want to add the type... but in the case of generic I want to add the DataType(Of T).Value. I hope there is some work-around but I don't think there is a way because the type of T can not be determined at compile time right? Ideally DirectCast(prop.getvalue(poco), DataType(Of T)).Value would be possible. This is when you hoped even more dynamics appear than whats in .NET 4.0.
It just occurred to me that I may have mis-read your question. Therefore another answer from me.
You can avoid the need to cast to a generic type (where you don't know the type parameter) at runtime by introducing a new interface that allows access to some Value property:
Public Interface IHasValue
Public ReadOnly Property Value As Object
End Interface
Next, make sure your DataType(Of T) class implements this interface. This means that each DataType(Of T) object (no matter what concrete type T is) is also an IHasValue, and you will be able to check this with TypeOf(…) Is IHasValue:
Public Class DataType(Of T) : Implements IHasValue
Public Value As T
Public ReadOnly Property UntypedValue As Object Implements IHasValue.Value
Get
Return Me.Value
End Get
End Property
End Class
(This duplication of Value is necessary for two reasons: First it is necessary when DataType(Of T).Value is a field instead of a property, and second because they do not have the same type.)
Then, where you fill your collection with fields, you can check whether a particular field is an DataType(Of T) (and if so, unwrap the Value from it) by doing the following:
Dim value As Object = prop.GetValue(poco)
If TypeOf(value) Is IHasValue Then ' <- is value a DataType(Of T) ? '
value = CType(value, IHasValue).Value ' <- if so, unwrap the value '
End If
Collection.Add(prop.Name, value)
This is probably more what you're after than my other answer, but let me emphasise this:
If TypeOf(…) Is … Then … is a code smell. Perhaps you should re-think your code and investigate solutions involving polymorphism.