简体   繁体   English

为什么我不能将类型约束的泛型参数转换为约束类型?

[英]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. 这对我自己来说更像是一个教育问题,因为我无法理解为什么.NET不允许这样做。

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 如果我有一个泛型类(Of T As IA, T2 As A)那么我有以下接口和实现基接口的类

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 因为我已经把T as IA为什么在add方法中是Dim foo4 As T = New A()不合法的时候

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 编辑:抱歉忘记了Class A ,错误信息请参见下文

Public Class A
  Implements IA
  Public Property A As String Implements IA.A
End Class

EDIT 2: Error was typed incorrectly 编辑2:错误输入错误

"Value of type class a cannnot be converted to T" “类型a的值不能转换为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> . 目前还不清楚你要做什么,但我注意到的一个问题是你似乎假设List<TypeThatImplementsIA>在某种程度上可以与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) . 想象一下A是一类飞鸟, IA是由可以飞行的生物实现的,有人创造了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> . 尽管AirplaneBlueJay都是可以飞行的东西,但是人们无法将BlueJay添加到List<Airplane> The one common situation in the Framework where one can use a GenericType<DerivedType> as a GenericType<BaseType> is with IEnumerable<T> . 框架中可以使用GenericType<DerivedType>作为GenericType<BaseType>的一种常见情况是使用IEnumerable<T> The reason for that is that one can't store T 's into an IEnumerable<T> --one can only read them out. 原因是人们无法将T存储到IEnumerable<T>中 - 只能读出它们。 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. 如果一个人期待一个IEnumerable<Animal>并且一个人得到一个IEnumerable<MaineCoonCat> ,那么每当一个人想要读一个Animal ,就会读取一个MainCoonCat实例,它继承自Animal ,因此可以替代它。 This feature of IEnumerable<T> is called covariance . IEnumerable<T>这个特性称为协方差

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. 对于每个非可空值类型,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 ). 另一种是从ValueType派生的堆对象类型(后者又从Object派生)。 Most .net languages will implicitly convert the former type to the latter, and allow code to explicitly convert the latter to the former. 大多数.net语言将隐式地将前一种类型转换为后者,并允许代码将后者明确地转换为前者。 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 . 协方差的前提是,例如IEnumerable<DerivedType>返回的每个对象都可以直接用作BaseType的实例, 而无需转换 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. 它不适用于结构类型实现的接口,也不适用于没有class约束的泛型。 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). 请注意,除非有特殊理由期望接口将由结构实现(例如IComparable<T> ,在许多情况下,接口不太可能由结构实现,因此class约束将是无害)。

That's because T is not the interface IA itself. 那是因为T不是接口IA本身。 It is one implementation of it. 这是它的一个实现。

Suppose that you have another class that implements IA : 假设您有另一个实现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: 然后你创建一个新的Generic Class实例,如下所示:

Dim genericObject as new GenericClass(Of B, A)

So in this case, T now is B , and A cannot be casted to B . 所以在这种情况下, T现在是BA不能转换为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. 为了能够实例化T ,有必要在类型定义中声明New约束。 So the generic class declaration would be: 所以泛型类声明将是:

Public Class GenericClass(Of T As {New, IA}, T2 As A)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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