Is it possibly to impose type constraints on a generic member of a class in an inheritance hierarchy?

I'm writing an application in C#, and am wrestling with its implementation of generics. I have an inheritance hierarchy that is mirrored by another inheritance hierarchy (Models and View Models) like so:

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);
    }
}

This works well enough, but doesn't enforce any type constraints on content, which leaves me casting it to the proper derived class every time I want to use it. I'd like to coax the compiler into enforcing the constraint that B objects only have B_Content content. My first cut at that was:

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);
    }
}

This works nicely, but means that I can't access the common elements of content when all I have is a collection of As. What I'd really like to do is something like:

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);
    }
}

This, however, produces an error complaining that B cannot be implicitly converted into an A. I've tried adding an explicit cast to no avail. Is there some way to express the constraints I'm looking for more elegantly than the second model?

Answers


It's not entirely clear what you're after. Are you trying to make it so that every instance of A has a Content property whose type is A_Content, every B has a Content property that's a B_Content, and so on? If so, you can't do that and have B/C/etc. inherit from A. (not in a non-smelly way, anyway). The signature of A says that the Content property should be able to get (and, presumably, set) any valid value of A_Content. You cannot change the return type of a function or the type of a property or field in a derived class. You could use generics to basically defer the typing of the property all the way down to the usage of the class, but that syntax will be ugly and I'm not certain what it gets you.

For example, you could do this:

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
}

But this means two things:

  • Anywhere you use A, B, or C you must specify the actual type of TContent (so A_Content, B_Content, etc.). Which is a pain
  • There is absolutely nothing stopping you from doing something like A<B_Content> (which is, in fact, essentially what B is in this case, since we've added nothing to the class).

In short, I think you need to drop back and punt and come up with a new design.

By the way

The reason your second example doesn't fly (with the List) is because you've told the list that it needs to contain A<A_Content>. Since B<B_Content> doesn't satisfy that, it won't work. This is a typical variance question and it confuses a lot of people. But consider this scenario (this code will not compile; it's intended to be demonstrative of the underlying reason):

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():

This would obviously break, since foo in reality is a B<B_Content>, so the runtime wouldn't let you set content equal to anything other than an instance of B_Content (or something that inherits from it), but the signature of the class means you should be able to assign anything that'sA_Content` or inherits from it.


Need Your Help

javascript create object using multiple variables for multiple property name

javascript json

how can i create object using multiple variables for multiple property name.

Bash grep avoid checking directories

bash grep

I'm getting errors because I search through sub directories and I don't want it to search those just the files that are in this folder. The other folders are LOGS and RESULTS and contain other fil...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.