0

我正在用 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这很好用,但意味着当我只有一个 s 的集合时,我无法访问公共元素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。我尝试添加显式强制转换无济于事。有什么方法可以比第二个模型更优雅地表达我正在寻找的约束吗?

4

4 回答 4

4

目前还不完全清楚你在追求什么。你是否试图让它的每个实例A都有一个Content类型为 的属性,A_Content每个实例都有一个为Ba 的Content属性,B_Content等等?如果是这样,你不能那样做并且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(so 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 实际上是 a B<B_Content>,所以运行时不会让您设置content为等于A_Content`的实例B_Content(或从它继承的东西)或从它继承的任何东西。, but the signature of the class means you should be able to assign anything that's

于 2012-04-10T20:37:04.107 回答
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>来保存BC对象的同质集合。

事实上,使用 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>,因此您可以使用 aList<IA<A_Content>>来保存对象的同质集合。

于 2012-04-10T20:41:41.857 回答
1

好吧,编译器会产生错误,因为确实B无法转换为A<A_Content>. 这是因为A<A_Content>不是超类B。类的父类BA<B_Content>.

恐怕你需要坚持铸造。这里需要它,因为您有As 列表。如果您真的想避免强制转换(我不确定您为什么要这样做),您可以尝试使用动态调度。

您可以尝试创建一个List<dynamic>而不是List<A>. 不过,您至少需要 C# 4.0。

于 2012-04-10T20:42:32.110 回答
0

希望我理解你的意图,所以

有这样的收藏

IList<A>意味着您希望拥有A具有不同实现场景的对象集合。

如果是基类型的属性,则该属性。这意味着基类型必须公开方法/属性 => 因此子类必须进行具体实现的状态和行为原语。

像这样的东西:

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 方法。

于 2012-04-10T20:43:23.843 回答