cast to generic type at runtime - vb.net

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.

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!

Why can't I cast a generic parameter with type constraint to the constrained type?

I am getting used to using interfaces, generics and develping them using inheritance in a real envrionment whilst trying use and implement this into a new architecture for one of our upcoming projects and I have a question regarding generics which I am confused about.
This is more of a educational question for myself because I can't understand why .NET doesn't allow this.
If I have a generic class which is (Of T As IA, T2 As A) then I have the following interfaces and class which implements the base interface
Public Interface IA
Property A As String
End Interface
Public Interface IB
inherits IA
Property B As String
End Interface
Public Class GenericClass(Of T As IA, T2 As A)
'Should be list of IA?
Public list As New List(Of T)
Public Sub Add()
End Sub
End Class
Because I have made T as IA why in the add method is Dim foo4 As T = New A() not legal when
Dim foo1 As IA = New A()
Dim foo2 As T
Dim foo3 = Activator.CreateInstance(Of T2)()
Dim x As IA = foo2
Dim y As IA = foo3
list.Add(x)
list.Add(y)
All of the above is? This is becoming a learning curve for me with generics etc. but I am just very confused with why I logically can't do this?
EDIT: Sorry forgot Class A and error message please see below
Public Class A
Implements IA
Public Property A As String Implements IA.A
End Class
EDIT 2: Error was typed incorrectly
"Value of type class a cannnot be converted to T"
It's not exactly clear what you're trying to do, but one problem I notice is that you seem to be assuming that a List<TypeThatImplementsIA> is somehow interchangeable with a List<IA>. That is not the case. Imagine that A were a class of flying birds, and IA were implemented by creatures that can fly, and someone created a GenericClass<Airplane, BlueJay). Even though Airplane and BlueJay are both things that can fly, one would not be able to add a BlueJay to a List<Airplane>. The one common situation in the Framework where one can use a GenericType<DerivedType> as a GenericType<BaseType> is with IEnumerable<T>. The reason for that is that one can't store T's into an IEnumerable<T>--one can only read them out. If one is expecting an IEnumerable<Animal> and one is given an IEnumerable<MaineCoonCat>, then every time one expects to read an Animal, one will read an instance of MainCoonCat, which inherits from Animal and may thus substitute for it. This feature of IEnumerable<T> is called covariance.
There's a limitation to such behavior, though, which stems from the fact that there is a difference between using an interface as a type of storage location (variable, parameter, etc.), versus using it as a constraint. For every non-nullable value type, there are actually two related types within the Runtime. One of them is a real value type, which has no concept of inheritance (but can implement interfaces). The other is a heap-object type which derives from ValueType (which in turn derives from Object). Most .net languages will implicitly convert the former type to the latter, and allow code to explicitly convert the latter to the former. Interface-type storage locations can only hold references to heap objects. This is significant because it means that while a struct which implements an interface is convertible to that interface type, that doesn't mean instance of the struct is an instance of that interface type. Covariance works on the premise that every object returned by e.g. an IEnumerable<DerivedType> may be used directly as an instance of BaseType without conversion. Such direct substitutability works with inherited class types, and with interfaces that are implemented by class types. It does not work with interfaces implemented by struct types, or with generics that do not have a class constraint. Adding a class constraint to a generic class type parameter will allow that type parameter to participate in covariance, but may preclude the use of structs as the generic type parameter. Note that unless one has particular reason to expect that an interface will be implemented by structures (as is the case with e.g. IComparable<T>, in many cases it's unlikely that an interface would be implemented by a structure and thus a classconstraint would be harmless).
That's because T is not the interface IA itself. It is one implementation of it.
Suppose that you have another class that implements IA:
Public Class B
Implements IA
Public Property B_A As String Implements IA.A
Public Property OtherProperty as Object
End Class
Then you create a new instance of Generic Class like this:
Dim genericObject as new GenericClass(Of B, A)
So in this case, T now is B, and A cannot be casted to B.
In this case instead, replacing the part of your doubt, a code that would make sense for me:
Dim foo4 As IA = New T()
EDIT due to comment
To be able to instantiate T, it is necessary to declare the New constraint in the type definition. So the generic class declaration would be:
Public Class GenericClass(Of T As {New, IA}, T2 As A)

How do I determine if a value is an instance of a generic type, ignoring the type parameter, in 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.

Can I qualify the type of a parameter in VB.NET?

This is kind of two questions (one more specific than the other).
If I have a method like this:
Public Function Blah(String Foo)
End Function
Can I qualify Foo against another type (for instance can I require that Foo be a String that also implements IInterface?).
I'm imagining something vaguely similar to this:
Public Function Blah(RandomObject Foo Where RandomObject Is IInterface)
End Function
Additionally, is there any way to qualify the Type parameter?
For instance, can I require that the Type I take as a parameter is of a particular class tree?
Public Function Blah(Type t Where Type Of String)
End Function
I should mention that I am using this in the context of a property of an attribute so the class declaration itself cannot be generic (this is purely focused on qualifying a method parameter rather than typing a class and its methods).
This looks like a case for generics to me. Your method signature would be something like this in VB.NET:
Public Function Blah(Of T As {IImplementedByT})(Foo As T)
This specifies that Foo can be of any type T as long as T implements IImplementedByT. Note that this method can be generic without the containing class needing to be generic. If you want T to be a class derived from RandomClass that also implements this interface, you can specify both constraints:
Public Function Blah(Of T As {RandomClass, IImplementedByT})(Foo As T)
You can do the first for a generic type, but not for a nongeneric type. Basically a variable (including a parameter) can only have one compile-time type, so you can't say "it has to be a Foo and an IBar - you have to pick one or the other. Generics let you say "it has to be some type T where T derives from Foo and implements IBar" though.
Generics is a huge topic - too big to cover in a Stack Overflow answer - but Microsoft has a good introductory article.
As for your second question - no, you can't do that. The Type value will only be known at execution time, so it has to be an execution time check. You can write that check fairly easily though, with Type.IsAssignableFrom.
Not sure what you mean by "Foo be a String that also implements IInterface".
string class is sealed, so you can't inherit from it & hence you cant implement an interface on top of it.
I hope I am on the right page.