在C#中构造通用数据类型

时间:2015-03-06 12:47:52

标签: c#

我遇到的问题是如何让我的SetContainer更通用,以包含任何扩展CompoundValue的类型。 这就是我尝试过的MapContainer<string, SetContainer<CompoundValue>>。 但我在state["a"] = new SetContainer<A>上收到错误,说A类型无法显式转换为CompoundValue。

我已经提供了一个相关示例,显示了下面的问题。

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

3 个答案:

答案 0 :(得分:3)

您可以像这样声明集合容器:

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

然而,这并没有解决问题。为什么?您可以使用源自T(或CompoundValue本身)的CompoundValue;但是,即使SetContainer<A>来自SetContainer<CompoundValue>,这也不会使A成为CompoundValue的后代。即,构造的泛型类型不接管其泛型类型参数的继承关系。因此,MapContainer<string, SetContainer<CompoundValue>>不接受SetContainer<A>作为值!

让我们举一个更简单的例子,让我们宣布一个这样的列表

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

这是不可能的,但我们假设它是。现在让我们在列表中添加值:

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

这似乎是可能的,因为AB继承自CompoundValue。但请记住,该列表实际上是List<A>,此列表需要存储AA的后代。 CompoundValueB都不是A s!

答案 1 :(得分:1)

您可以从SetContainer派生并在派生类中定义约束。而不是使用SetContainer,您可以使用带有约束的派生类。

首先,您将从SetContainer定义您的派生:

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

之后,您将在任何地方使用容器而不是SetContainer:

MapContainer<string, YourContainer<CompoundValue>> state;

现在,您可以使用容器初始化您的成员:

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

而不是添加SetContainer,您现在可以添加已定义类型约束的Container。

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

答案 2 :(得分:0)

你想做什么是不可能的。即使B类来自A,也不意味着Foo&lt; B&gt;与Foo&lt; A&gt;兼容。

尝试编译:

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

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

您需要更改设计。尝试使用包装类。

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