C# - 一个列表中包含多个泛型类型

191

这可能是不可能的,但我有这个类:

public class Metadata<DataType> where DataType : struct
{
    private DataType mDataType;
}

这里还有更多内容,但我们先保持简单。使用where语句限制了通用类型(DataType)仅限于值类型。我想做的是有一个不同类型(DataType)的元数据对象列表,例如:

List<Metadata> metadataObjects;
metadataObjects.Add(new Metadata<int>());
metadataObjects.Add(new Metadata<bool>());
metadataObjects.Add(new Metadata<double>());

这是否可能呢?


30
我想知道下面的答案方法与仅使用List<object>相比是否有任何真正的好处?它们不会停止装箱/拆箱,也不会消除需要强制转换的需求,最终,您得到的是一个关于实际DataType的信息。我正在寻找解决这些问题的方法。如果你只是为了把实现/派生泛型类型放在一个泛型列表中而声明一个接口/类,那么这与使用List<object>有何不同,除了多一层无意义的包装? - Saeb Amini
10
抽象基类和接口都通过限制可添加到列表中的元素类型来提供一定程度的控制。我也不明白装箱是如何与此相关的。 - 0b101010
3
当然,如果你使用的是.NET v4.0或更高版本,那么协变就是解决方案。 List<Metadata<object>> 可以达到效果。 - 0b101010
2
@0b101010 我也是这么想的,但不幸的是,值类型上不允许使用 variance。由于 OP 有一个 struct 约束,所以在这里它行不通。请参阅 - nawfal
1
@0b101010,两者都只限制引用类型,任何内置值类型和任何结构体仍然可以添加。此外,在最后,您将得到一个MetaData引用类型列表,而不是原始值类型,并且没有有关每个元素的基础值类型的(编译时)信息,这实际上就是“装箱”。 - Saeb Amini
3个回答

236
public abstract class Metadata
{
}

// extend abstract Metadata class
public class Metadata<DataType> : Metadata where DataType : struct
{
    private DataType mDataType;
}

10
相较于简单的 List<object>,这种方法有什么好处吗?请查看我在原帖问题下发布的评论。 - Saeb Amini
@leppie:是否可以将多个“Metadata”实例添加到列表中? - Khoi
16
一个List<object>无法表达开发者的意图,也无法防止开发者因错误地将一些非MetaData对象添加到列表中而自我伤害。 通过使用List<MetaData>,人们可以理解列表应该包含什么。很可能MetaData将具有一些未在上面示例中显示的公共属性/方法。通过object访问这些属性/方法需要繁琐的强制转换。 - Buzz
通过使用@SaebAmini,您可以将“untyped”成员放入基类并直接通过数组访问它们。只有由通用类型绑定的成员需要在子类中。 - Dave Cousineau
2
  • 回答正确的问题
  • 拒绝进一步阐述
  • 离开
  • 插入 Giga Chad 模因 *
- Jotarata
显示剩余4条评论

101

跟随leppie的答案,何不将MetaData设计为一个接口:

public interface IMetaData { }

public class Metadata<DataType> : IMetaData where DataType : struct
{
    private DataType mDataType;
}

有人能告诉我为什么这种方法更好吗? - Lazlo
41
既然没有共同的功能需要分享,为什么还要浪费一个基类呢?使用接口就足够了。 - flq
2
因为你可以在结构体中实现接口。 - Damian Leszczyński - Vash
2
使用虚方法的类继承,速度大约比接口方法快1.4倍。因此,如果您计划在MetaData<DataType>中实现任何非泛型元数据(虚拟)方法/属性,请选择抽象类而不是接口,如果性能是一个问题。否则,使用接口可能更加灵活。 - TamusJRoyce

41

我也使用过非通用版本,使用 new 关键字:

public interface IMetadata
{
    Type DataType { get; }

    object Data { get; }
}

public interface IMetadata<TData> : IMetadata
{
    new TData Data { get; }
}

显式接口实现用于允许同时使用 Data 成员:

public class Metadata<TData> : IMetadata<TData>
{
    public Metadata(TData data)
    {
       Data = data;
    }

    public Type DataType
    {
        get { return typeof(TData); }
    }

    object IMetadata.Data
    {
        get { return Data; }
    }

    public TData Data { get; private set; }
}

您可以派生一个针对值类型的版本:

public interface IValueTypeMetadata : IMetadata
{

}

public interface IValueTypeMetadata<TData> : IMetadata<TData>, IValueTypeMetadata where TData : struct
{

}

public class ValueTypeMetadata<TData> : Metadata<TData>, IValueTypeMetadata<TData> where TData : struct
{
    public ValueTypeMetadata(TData data) : base(data)
    {}
}

这可以扩展到任何类型的通用约束。


8
+1只是因为你展示了如何使用它(DataTypeobject Data帮了很多忙)。 - Odys
4
我似乎无法编写例如Deserialize<metadata.DataType>(metadata.Data);这样的代码。它告诉我“无法解析符号metadata”。如何检索DataType以在泛型方法中使用它? - Cœur
1
@Cœur 有点晚了,但你的问题在如何使用反射调用泛型方法?中得到了解答。(为自己和其他通过搜索到达此处的人添加评论)。 - Heretic Monkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接