简体   繁体   中英

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 eg 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 eg IComparable<T> , in many cases it's unlikely that an interface would be implemented by a structure and thus a class constraint 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM