我正在尝试为抽象类实现一些通用方法,如下所示:
public abstract class MyAbstractClass : MyBaseObject {
public MyAbstractClass() : base() { } // there is a parameterless constructor...
}
public class MyList<T> where T : MyBaseObject, new() {
// a generic container that is designed for the base class
}
//--- some paint control of mine
public class PaintControl : IDisposable {
public void InitDrawItems(MyList<MyAbstractClass> items) {
// paint items => this is where the compilation error occurs...
}
}
我收到以下编译错误:
错误24'MyAbstractClass'必须是具有公共无参数构造函数的非抽象类型,以便在通用类型或方法'MyList'中将其用作参数'T'
当然,我想使用抽象的MyAbstractClass类(该类有多个子项来相应地处理绘画)。 有办法解决吗?
编辑:我确实使类Abstract完全确保子代确实实现了抽象方法。
答案 0 :(得分:2)
这是由generic type variance引起的。声明一个具体类型实际上会在编译时创建一个新类型。类中的 type 参数不能在类型之间隐式转换。 MyList<MyBaseObject>
和MyList<MyConcreteObject>
之间没有继承关系。
仅通用接口或委托允许转换。
有两种方法可以解决此问题-使用接口而不是具体的类,例如:
class MyList<T>:IList<T> where T : MyBaseObject, new()
{
}
class PaintControl {
public void InitDrawItems<T>(IList<MyAbstractClass> items) //where T:MyAbstractClass,new()
{
//var anItem=new T();
}
}
或者将InitDrawItems
本身设为通用:
public void InitDrawItems<T>(MyList<T> items) where T:MyAbstractClass,new()
{
// No compilation errors here
}
答案 1 :(得分:0)
您应该删除new()
中T
类型的MyList
约束,因为它指定只能使用可以初始化的类型,而不能使用抽象的类型。
您可以在此处查看更多详细信息:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
答案 2 :(得分:0)
new()不允许将抽象类和接口用作T
,因为它们不可实例化。 new()
表示泛型类型必须声明一个适合对象实例化的公共无参数构造函数。
因此,我认为您有一个选择:
如果可以的话,请删除new()
子句,并且可以将MyBaseObject
派生的任何抽象类型与泛型一起使用。由于您的课程确实是抽象的,并且是MyBaseObject
的子级,因此效果很好。
答案 3 :(得分:0)
正如@Martin在评论中从docs中指出的:
新约束指定通用类声明中的任何类型参数都必须具有公共的无参数构造函数。 要使用新的约束,类型不能为抽象。
以及您编写的用于定义MyList<T>
的代码:
public class MyList<T> where T : MyBaseObject, new()
说MyList<T>
是一个泛型类,其中T必须具有新的约束(可以实例化的类型)。
所以您的代码上的编译错误的原因:
public void InitDrawItems(MyList<MyAbstractClass> items)
因为在这种情况下,T
是MyAbstractClass
,它是无法实例化的抽象类。
因此,您的选择如下:
使方法通用,如下所示:
public void InitDrawItems<T>(MyList<T> items) where T : MyAbstractClass, new()
{
// paint items => this is where the compilation error occurs...
}
从T
的{{1}}中删除新约束
MyList
设为普通类,而不是抽象类。答案 4 :(得分:0)
答案似乎有些变通,但实际上很有道理...
我确定知道要使用列表中的哪个项目(我正在遍历所有项目)。由于已创建此项目,因此肯定是非抽象类。因此,我可以调用该项目的激活器,而不是new T()
:
public void InitDrawItems(MyList<MyAbstractClass> items)
foreach (var item in items) {
Type type = o.GetType();
// type is definitely NOT abstract, since the object was created, so it is a
// non-abstract child class and I create the right "copy" of the item !
MyAbstractClass newItem = Activator.CreateInstance(type) as MyAbstractClass;
// ... do whatever I need with the item
}
}
注意:对于泛型,抽象类不允许调用new T()
。这么说:由于抽象父类不能被实例化,所以通用抽象项的容器包含派生子类的项。因此,如果编译器允许,调用new T()
肯定会导致问题:应该是哪个子类?!?
答案 5 :(得分:0)
这非常简单,您无法实例化抽象类,因此T = MyAbstractClass
与where T : new()
约束冲突。