user3139545
user3139545

Reputation: 7374

Constructing generic datatypes in C#

The problem im having is how to make my SetContainer more generic to contain any type that extends CompoundValue. This is what I have tried MapContainer<string, SetContainer<CompoundValue>>. But im getting an error on state["a"] = new SetContainer<A> saying that the type A can not be explicitly converted to CompoundValue.

I have included a relevant example showing the problem below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

using Microsoft.Modeling; // SetContainer and MapContainer

namespace SGSN
{
    class ControlState
    {
        MapContainer<string, SetContainer<CompoundValue>> state;

        ControlState()
        {
            state = new MapContainer<string, SetContainer<CompoundValue>>();

            state["a"] = new SetContainer<A>(); //ERROR
            state["b"] = new SetContainer<B>(); //ERROR
            state["c"] = new SetContainer<C>(); //ERROR
            state["d"] = new SetContainer<D>(); //ERROR
        }
    }

    class A: CompoundValue
    {
        internal string a1;
        internal string a2;
        internal string a3;
        internal string a4;
        internal string a5;
        internal string a6;

        internal A(string a1, string a2, string a3,
            string a4, string a5, string a6)
        {
            this.a1= a1;
            this.a2= a2;
            this.a3= a3;
            this.a4= a4;
            this.a5= a5;
            this.a6= a6;
        }
    }

    class B: CompoundValue
    {
        internal string b1;
        internal string b2;
        internal string b3;

        internal B(string b1, string b2, string b3)
        {
            this.b1= b1;
            this.b2= b2;
            this.b3= b3;
        }
    }

    class C: CompoundValue
    {
        internal string c1;
        internal string c2;
        internal string c3;
        internal string c4;
        internal string c5;
        internal string c6;
        internal string c7;
        internal string c8;

        internal C(string c1, string c2, string c3,
            string c4, string c5, string c6, string c7, string c8)
        {
            this.c1 = c1;
            this.c2= c2;
            this.c3= c3;
            this.c4= c4;
            this.c5 = c5;
            this.c6= c6;
            this.c7= c7;
            this.c8= c8;
        }
    }

    class D: CompoundValue
    {
        internal string d1;
        internal string d2;
        internal string d3;

        internal D(string d1, string d2, string d3)
        {
            this.d1= d1;
            this.d2= d2;
            this.d3= d3;
        }
    }
}

Upvotes: 0

Views: 111

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

You could declare the set container like this:

public class SetContainer<T> 
    where T : CompoundValue
{ ... }

However this does not solve the problem. Why? You can use a T that derives from CompoundValue (or CompoundValue itself); however, this does not make SetContainer<A> a descendant of SetContainer<CompoundValue>, even if A derives from CompoundValue. I.e., the constructed generic type does not take over the inheritance relation of its generic type argument. Therefore a MapContainer<string, SetContainer<CompoundValue>> does not accept a SetContainer<A> as value!

Let's take a simpler example and let's declare a list like this

List<CompoundValue> list = new List<A>(); // Not possible!

This is not possible, but let's assume it was. Now let's add values to the list:

list.Add(new CompoundValue()); // Error!
list.Add(new A());             // OK
list.Add(new B());             // Error!

This seems to be possible because A and B inherit from CompoundValue. But remember, the list is in reality a List<A> and this list wants A or descendants of A to be stored. Neither CompoundValue nor B are As!

Upvotes: 3

ilitirit
ilitirit

Reputation: 16352

What you want to do is not possible. Even if a class B derives from A, it doesn't mean that Foo<B> is compatible with Foo<A>.

Try compiling this:

class Foo<T> {}
class Bar {}
class Baz : Bar {}

void Main()
{
    var list = new List<Foo<Bar>>();
    list.Add(new Foo<Baz>());
}

You will need to change your design. Try using a wrapper class.

class Foo<T> 
{
    public T Item { get; set; }
    public Foo(T t)
    {
        Item = t;
    }
}

class Bar 
{
    public override string ToString()
    {
        return "Bar";
    }
}

class Baz : Bar 
{
    public override string ToString()
    {
        return "Baz";
    }
}

class BarHolder 
{
    public Bar BarItem { get; set; }
    public BarHolder(Bar bar)
    {
        BarItem = bar;
    }
}

void Main()
{
    var wrappedBar = new BarHolder(new Bar());
    var wrappedBaz = new BarHolder(new Baz());

    var barList = new List<Foo<BarHolder>>();
    barList.Add(new Foo<BarHolder>(wrappedBar));
    barList.Add(new Foo<BarHolder>(wrappedBaz));

    foreach (var obj in barList)
    {
        Console.WriteLine(obj.Item.BarItem);
    }
}

Upvotes: 0

pgenfer
pgenfer

Reputation: 622

Your could derive from SetContainer and define your constraint in your derived class.Instead of using a SetContainer then you could use your derived class with the constraint.

So first you would define your derivation from SetContainer:

public class YourContainer<T> : SetContainer<T>  where T : CompoundValue
{
   // no code needed here
}

After that, you would use your container everywhere instead of the SetContainer:

MapContainer<string, YourContainer<CompoundValue>> state;

Now you can inialize your member with your container:

state = new MapContainer<string, YourContainer<CompoundValue>>();

And instead of adding a SetContainer, you can now add your Container which has the type constraint defined.

state["a"] = new YourContainer<A>();

Upvotes: 1

Related Questions