【问题标题】:serialize list of generic type序列化泛型列表
【发布时间】:2013-10-15 09:49:35
【问题描述】:

我正在编写一个用于序列化包含任何数据类型的通用列表的应用程序。所以我设计了一个基本数据类型来填充列表,如下所示:

public abstract class GenericObject<T> {
    public string key;
    public T value;

    public GenericObject() { }
    public GenericObject(string key, T value) : this() { 
        this.key = key;
        this.value = value;
    }
}

还有一个类GenericList,它实现了IXmlSerializable-Interface来写一个键值对,如下所示:

GenericObject<int> myInt = new GenericObject<int>("key", 3);

这将产生以下 XML:

<key>3</key>

GenericList 的类定义大致如下:

public class GenericList<T> : IXmlSerializable {
    List<T> objects;
    // ...
}

假设我们有一个派生自GenericObject&lt;string&gt; 的类Person(不管它看起来如何),并且我们想用几个人来填充列表。我遇到的问题是在GenericList-class 的泛型类型 T 上定义一个约束,以便只有从GenericObject 派生的类型才可能。我已经尝试过使用public class GenericList&lt;T&gt; : IXmlSerializable where T : GenericObject&lt;object&gt;,但由于以下编译器错误,它不起作用:

Person' cannot be used as type parameter 'T' in the generic type or method 'GenericList<T>'. There is no implicit reference conversion from 'Person' to 'GenericObject<object>'

我还尝试将 where 子句留空,但随后我必须检查 GenericList 中的 T 的类型,以便获取它的键和值,其中我也失败了以下内容:

if (typeof(T).IsAssignableFrom(typeof(GenericObject<object>))) 

因为T 的类型是Person 而不是GenericObject&lt;object&gt;,所以它总是返回false。

有人可以建议我如何用一个人填写我的列表吗?

【问题讨论】:

    标签: c# generics xml-serialization


    【解决方案1】:

    您可以使用covariance。由于变体类型参数只能在接口和委托中声明(不能在类定义中),因此您还需要定义一个接口:

    public interface IGenericObject<out T>
    {
        string Key { get; }
        T Value { get; }
    }
    
    public abstract class GenericObject<T> : IGenericObject<T>
    {
        public string Key { get; set; }
        public T Value { get; set; }
    
        protected GenericObject() { }
    
        protected GenericObject(string key, T value)
            : this()
        {
            this.Key = key;
            this.Value = value;
        }
    }
    
    public class GenericList<TGenericObject> : IXmlSerializable
        where TGenericObject : IGenericObject<object>
    {
        private readonly List<TGenericObject> _list = new List<TGenericObject>();
    
        public void Add(TGenericObject item)
        {
            _list.Add(item);
        }
    
        public XmlSchema GetSchema()
        {
            // ...
        }
    
        public void ReadXml(XmlReader reader)
        {
            // ...
        }
    
        public void WriteXml(XmlWriter writer)
        {
            // ...
        }
    }
    
    public class Person : GenericObject<string>
    {
    
    }
    

    现在你可以这样做了:

    public class SomeClass
    {
        public void SomeMethod()
        {
            Person somePerson = new Person();
    
            GenericList<IGenericObject<object>> listWithGenericsOfObject = new GenericList<IGenericObject<object>>();
            listWithGenericsOfObject.Add(somePerson);
    
            GenericList<IGenericObject<string>> listWithGenericsOfString = new GenericList<IGenericObject<string>>();
            listWithGenericsOfString.Add(somePerson);
    
            GenericList<Person> listWithGenericsOfPerson = new GenericList<Person>();
            listWithGenericsOfPerson.Add(somePerson);
        }
    }
    

    现在您无需在运行时使用 IsAssignableFrom 检查类型。但是,如果您需要它,您应该像这样交换类型:

    typeof(GenericObject<object>).IsAssignableFrom(typeof(T))
    

    【讨论】:

    • 哇,非常好的解决方案,虽然我不完全知道它为什么起作用 :D 如果可以的话,我会给这个答案 100 票
    • 要了解它为什么起作用,我认为您需要阅读有关方差(特别是协方差)的信息。接口中的 'out'-keyword 指定类型参数是协变的。
    猜你喜欢
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    • 1970-01-01
    • 2020-10-08
    • 2011-07-29
    相关资源
    最近更新 更多