我正在用C#编写应用程序,并正在努力实现泛型。我有一个继承层次结构,由另一个继承层次结构(模型和视图模型)镜像,如下所示:
class A_Content { }
class B_Content : A_Content
{
public string Bar;
}
class C_Content : A_Content
{
public string Foo;
}
class A { public A_Content content; }
class B : A { }
class C : A { }
public class Test
{
IList<A> A_Collection = new List<A>();
public Test()
{
B b = new B();
C c = new C();
b.content = new B_Content();
c.content = new C_Content();
A_Collection.Add(b);
A_Collection.Add(c);
}
}
这很好用,但是没有对content
强制执行任何类型约束,这使我每次想要使用它时都将它转换为正确的派生类。我想哄骗编译器强制执行B对象只有B_Content
内容的约束。我的第一个切入点是:
class A_Content { }
class B_Content : A_Content
{
public string Bar;
}
class C_Content : A_Content
{
public string Foo;
}
class A { }
class B : A { B_Content content; }
class C : A { C_Content content; }
public class Test
{
IList<A> A_Collection = new List<A>();
public Test()
{
B b = new B();
C c = new C();
A_Collection.Add(b);
A_Collection.Add(c);
}
}
这很好用,但这意味着当我拥有content
的集合时,我无法访问A
的公共元素。我真正想做的是:
abstract class A_Content { }
class B_Content : A_Content
{
public string Bar;
}
class C_Content : A_Content
{
public string Foo;
}
abstract class A<T> { T content; }
class B : A<B_Content> { }
class C : A<C_Content> { }
public class Test {
IList<A<A_Content>> A_Collection = new List<A<A_Content>>();
public Test()
{
B b = new B();
C c = new C();
A_Collection.Add(b);
A_Collection.Add(c);
}
}
然而,这会产生一个错误,抱怨B不能隐式转换为A.我试过添加一个显式的强制转换无效。有没有办法表达我想要比第二个模型更优雅的约束?
答案 0 :(得分:4)
你所追求的并不完全清楚。您是否尝试将A
的每个实例都具有类型为Content
的{{1}}属性,每个A_Content
都有一个B
属性,即{ {1}},依此类推?如果是这样,您就无法和拥有Content
/ B_Content
/ etc。继承自B
。 (无论如何,不是以非臭的方式)。 C
的签名表示A
属性应该能够获得(并且可能设置)A
的任何有效值。您不能更改函数的返回类型或派生类中的属性或字段的类型。你可以使用泛型来基本上将属性的输入推迟到类的使用,但是这种语法会很难看,而且我不确定它会给你带来什么。
例如,你可以这样做:
Content
但这意味着两件事:
A_Content
,public class A<TContent> where TContent : A_Content
{
public TContent Content { get; set; }
}
public class B<TContent> : A<TContent> where TContent : B_Content
{
// nothing here, as the property is already defined above in A
}
public class C<TContent> : A<TContent> where TContent : C_Content
{
// nothing here, as the property is already defined above in A
}
或A
的任何地方,您必须指定B
的实际类型(C
,TContent
,等等。)。哪个是痛苦A_Content
这样的事情(实际上,在这种情况下基本上是B_Content
,因为我们没有在课程中添加任何东西)。简而言之,我认为你需要退后一步并提出新的设计。
顺便说一下
您的第二个示例未启动的原因(使用A<B_Content>
)是因为您已告诉列表它需要包含B
。由于List
不满足,因此不起作用。这是典型的差异问题,它会混淆人们的很多。但请考虑这种情况(此代码将无法编译;它旨在说明潜在原因):
A<A_Content>
这显然会中断,因为B<B_Content>
实际上是List<A<A_Content>> list = new List<A<A_Content>>();
list.Add(new B()); // this seems OK so far, right?
A<A_Content> foo = list[0];
foo.content = new A_Content():
,所以运行时不允许你将foo
设置为等于B<B_Content>
的实例(或从其继承的内容)content
A_Content`或从其继承。
答案 1 :(得分:3)
您可以使用接口,以及接口成员的显式实现:
abstract class A_Content {}
class B_Content : A_Content {}
class C_Content : A_Content {}
interface IA
{
A_Content content { get; }
}
abstract class A<T> : IA
where T : A_Content
{
T content;
A_Content.content { get { return this.content; } }
}
class B : A<B_Content> {}
class C : A<C_Content> {}
然后,您可以设置List<IA>
来保存B
和C
个对象的同类集合。
事实上,使用C#4及更高版本,您可以使界面具有通用性和协变性;然后你可以隐式地实现接口(只要你使用属性而不是字段):
interface IA<out T>
{
T content { get; }
}
abstract class A<T> : IA<T>
where T : A_Content
{
T content { get; set; }
}
class B : A<B_Content> {}
class C : A<C_Content> {}
现在,B
仍然无法转换为A<A_Content>
,但可以转换为IA<A_Content>
,因此您可以使用List<IA<A_Content>>
保持你的同类物品集合。
答案 2 :(得分:1)
好吧,编译器会产生错误,因为确实B
无法转换为A<A_Content>
。
这是因为A<A_Content>
不是B
的超类。 B
类的父类是A<B_Content>
。
恐怕你需要坚持施法。这里需要它,因为你有A
的列表。
如果你真的想避免投射(我不确定你为什么这么想),你可以试试动态调度。
您可以尝试创建List<dynamic>
而不是List<A>
。
但是,你至少需要C#4.0。
答案 3 :(得分:0)
希望我正确地承担你的意图,所以
拥有这样的集合
IList<A>
表示您希望拥有一组A
个具有不同实现方案的对象。
该属性是否为基类型的属性。这意味着基类型必须公开方法/属性=&gt;所以状态和行为原语,子类必须进行具体的实现。
这样的事情:
class A_Content { public virtual string Bar {get;set;} }
class B_Content : A_Content
{
public override string Bar {get;set;};
}
class C_Content : A_Content
{
public override string Bar {get;set};
}
以及代码中的某个地方:
public Test()
{
B b = new B();
C c = new C();
A_Collection.Add(b);
A_Collection.Add(c);
//so
A_Collection[0].Bar // B::Bar
A_Collection[1].Bar //C::Bar
}
而且你不需要投射到真实物体。简单的OOP方法。