【问题标题】:c# - How to deserialize a generic list<T> when I don't know the type of (T)?c# - 当我不知道 (T) 的类型时,如何反序列化通用列表<T>?
【发布时间】:2010-12-20 18:19:49
【问题描述】:

出于听觉原因,我使用二进制格式化程序将业务方法的参数存储到数据库中。

问题是,当参数是通用列表时,我找不到转换反序列化对象的方法,因为我不知道类型,或者如果我知道类型,我不知道如何转换运行时的对象。

有人知道如何在运行时动态地转换包含通用列表的对象吗?

我需要这样做,因为我需要在属性网格中显示反序列化的数据:

object objArg = bformatter.Deserialize(memStr);

//If the type is a clr type (int, string, etc)
if (objArg.GetType().Module.Name == "mscorlib.dll")
{                 
    //If the type is a generic type (List<>, etc) 
    //(I'm only use List for these cases)
    if (objArg.GetType().IsGenericType)
    {
         // here is the problem
         pgArgsIn.SelectedObject = new { Value = objArg};                    

         //In the previous line I need to do something like... 
         //new { Value = (List<objArg.GetYpe()>) objArg};
     }
     else
     {
         pgArgsIn.SelectedObject = new { Value = objArg.ToString() };                    
     }
}
else
{
    //An entity object
    pgArgsIn.SelectedObject = objArg;                
}

【问题讨论】:

  • “听觉原因”?在这种情况下这意味着什么?
  • 为了遵守审计法规,我想。
  • 对于信息,如果您更改对象,BinaryFormatter 会非常脆弱。我会推荐一个基于契约的序列化器,例如 XmlSerializer、DataContractSerializer 或(对于二进制)protobuf-net。
  • 回复您的评论;有一种方法可以通过自定义描述符来满足您的要求;吃完(一会儿)我会写一个例子。
  • 添加了不涉及查找 T 的示例

标签: c# generics list ilist serialization


【解决方案1】:

如果您使用的序列化程序不保留类型 - 至少,您必须将 T 的类型与数据一起存储,并使用它来反射地创建通用列表:

//during storage:
Type elementType = myList.GetType().GetGenericTypeDefinition().GetGenericArguments[0];
string typeNameToSave = elementType.FullName;

//during retrieval
string typeNameFromDatabase = GetTypeNameFromDB();
Type elementType = Type.GetType(typeNameFromDatabase);
Type listType = typeof(List<>).MakeGenericType(new Type[] { elementType });

现在你有了listType,这正是你使用的List&lt;T&gt;(比如List&lt;Foo&gt;)。您可以将该类型传递到您的反序列化例程中。

【讨论】:

  • 是的!这是一个很好的方法,我还没有解决方案。我有反序列化的对象,我有泛型类型 (List)。现在,我需要在运行时强制转换对象,我需要做类似 MyTypedObjectArg = objArg as List;为什么?..如果我将属性网格绑定到我得到的对象:Value = [System.Collection.Generic.List`1],但如果我设置类型化对象我有一个集合,我可以看到里面的实体和里面的属性实体等
  • typeof(List&lt;&gt;).MakeGenericType(new Type[] { elementType }); 为我解决了
【解决方案2】:

使用BinaryFormatter,您不需要需要知道类型;元数据包含在流中(使其更大,但是嘿!)。但是,除非您知道类型,否则您不能 cast。通常在这种情况下,您必须使用常见的已知接口(非通用IList 等)和反射。还有很多。

我也想不出知道在PropertyGrid 中显示的类型的巨大要求 - 因为它接受object,所以只需给它BinaryFormatter 提供的内容。您在那里看到的具体问题是什么?同样,您可能想要检查 IList(非通用) - 但不必担心 IList&lt;T&gt;,因为这不是 PropertyGrid 检查的内容!

如果您想要 (like so),当然可以找到T - 并使用MakeGenericType()Activator.CreateInstance - 不漂亮。


好的;这是一种使用自定义描述符的方法,它涉及了解对象或列表类型的任何事情;如果您真的希望可以将列表项直接展开到属性中,那么在此示例中您会看到 2 个假属性(“Fred”和“Wilma”) - 这是额外的工作,虽然;-p

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }

    public override string ToString() {
        return Name;
    }
}

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Person fred = new Person();
        fred.Name = "Fred";
        fred.DateOfBirth = DateTime.Today.AddYears(-23);
        Person wilma = new Person();
        wilma.Name = "Wilma";
        wilma.DateOfBirth = DateTime.Today.AddYears(-20);

        ShowUnknownObject(fred, "Single object");
        List<Person> list = new List<Person>();
        list.Add(fred);
        list.Add(wilma);
        ShowUnknownObject(list, "List");
    }
    static void ShowUnknownObject(object obj, string caption)
    {
        using(Form form = new Form())
        using (PropertyGrid grid = new PropertyGrid())
        {
            form.Text = caption;
            grid.Dock = DockStyle.Fill;
            form.Controls.Add(grid);
            grid.SelectedObject = ListWrapper.Wrap(obj);
            Application.Run(form);
        }
    }
}

[TypeConverter(typeof(ListWrapperConverter))]
public class ListWrapper
{
    public static object Wrap(object obj)
    {
        IListSource ls = obj as IListSource;
        if (ls != null) obj = ls.GetList(); // list expansions

        IList list = obj as IList;
        return list == null ? obj : new ListWrapper(list);
    }
    private readonly IList list;
    private ListWrapper(IList list)
    {
        if (list == null) throw new ArgumentNullException("list");
        this.list = list;
    }
    internal class ListWrapperConverter : TypeConverter
    {
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        public override PropertyDescriptorCollection GetProperties(
            ITypeDescriptorContext context, object value, Attribute[] attributes) {
            return new PropertyDescriptorCollection(
                new PropertyDescriptor[] { new ListWrapperDescriptor(value as ListWrapper) });
        }
    }
    internal class ListWrapperDescriptor : PropertyDescriptor {
        private readonly ListWrapper wrapper;
        internal ListWrapperDescriptor(ListWrapper wrapper) : base("Wrapper", null)
        {
            if (wrapper == null) throw new ArgumentNullException("wrapper");
            this.wrapper = wrapper;
        }
        public override bool ShouldSerializeValue(object component) { return false; }
        public override void ResetValue(object component) {
            throw new NotSupportedException();
        }
        public override bool CanResetValue(object component) { return false; }
        public override bool IsReadOnly {get {return true;}}
        public override void SetValue(object component, object value) {
            throw new NotSupportedException();
        }
        public override object GetValue(object component) {
            return ((ListWrapper)component).list;
        }
        public override Type ComponentType {
            get { return typeof(ListWrapper); }
        }
        public override Type PropertyType {
            get { return wrapper.list.GetType(); }
        }
        public override string DisplayName {
            get {
                IList list = wrapper.list;
                if (list.Count == 0) return "Empty list";

                return "List of " + list.Count
                    + " " + list[0].GetType().Name;
            }
        }
    }
}

【讨论】:

  • 嗨 Marc,我不知道为什么,但是如果我这样设置属性网格:pgArgsIn.SelectedObject = new { Value = arg}; //当arg是反序列化的参数时。我在属性网格中有这样的东西:Value = [system.collections....],没有任何东西让我有机会知道对象里面有什么。但是,如果我这样设置属性网格: pgArgsIn.SelectedObject = new { Value = arg as List};我在属性网格中有:Value = (Collection) [...] 带有一个按钮,可以显示具有她属性的实体列表。
  • 然后,我需要在运行时强制转换反序列化的对象。激活器和其他东西返回一个对象,然后我没有我需要的东西。
  • 你认为你为什么需要投? PropertyGrid 不关心你的变量类型...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
相关资源
最近更新 更多