2009-11-26 29 views
5

por razones auditivas. Almaceno los argumentos de los métodos comerciales serializados en la base de datos utilizando el formato binario.C# - ¿Cómo deserializar una lista genérica <T> cuando no sé el tipo de (T)?

El problema es que cuando un argumento es una lista genérica, no encuentro la manera de lanzar el objeto deserializado porque no sé el tipo, o si sabré el tipo, no sé cómo lanzar el objeto en tiempo de ejecución.

¿Alguien sabe cómo lanzar dinamicamente un objeto que contiene una lista genérica en el tiempo de ejecución?

Necesito hacer esto porque necesito mostrar los datos deserializados en una cuadrícula de propiedades:

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

"razones auditivo"? ¿Qué significa esto en este contexto? –

+0

Para el cumplimiento de las normas de auditoría, me imagino. – AakashM

+0

Para información, 'BinaryFormatter' es bastante frágil si cambias tus objetos. Recomendaría un serializador basado en contrato como XmlSerializer, DataContractSerializer o (para binario) protobuf-net. –

Respuesta

3

Con BinaryFormatter usted no necesita necesita para saber el tipo; los metadatos están incluidos en la transmisión (haciéndolo más grande, pero ¡oigan!). Sin embargo, no puede emitir a menos que sepa el tipo. A menudo, en este escenario, debe usar interfaces conocidas comunes (no genéricas IList, etc.) y reflexión. Y mucha cantidad

Tampoco puedo pensar en un gran requisito para saber qué tipo mostrar en un PropertyGrid - dado que esto acepta object, simplemente proporciónele lo que BinaryFormatter proporciona. ¿Hay algún problema específico que estés viendo allí? Una vez más, es posible que desee comprobar si hay IList (no genérico) - pero no vale la pena preocuparse IList<T>, ya que esto no es lo PropertyGrid comprueba!

Por supuesto, puede encontrar el T si desea (like so) - y el uso de MakeGenericType() y Activator.CreateInstance - no es bonito.


Aceptar; esta es una forma de usar descriptores personalizados que no incluye implica saber cualquier cosa sobre el objeto o el tipo de lista; si realmente desea, es posible ampliar los elementos de la lista directamente en las propiedades, por lo que en este ejemplo se vería 2 propiedades falsas ("Fred" y "Wilma") - que es un trabajo extra, aunque ;-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; 
      } 
     } 
    } 
} 
+0

Hola Marc, no sé por qué, pero si configuro la grilla de propiedad como esto: pgArgsIn.SelectedObject = new {Valor = arg}; // Cuando arg es el argumento deserializado. Tengo algo como esto en la grilla de propiedad: Value = [system.collections ....], sin nada más que me brinde la oportunidad de saber qué tiene el objeto dentro. Pero, si fijo la red propiedad como esta: pgArgsIn.SelectedObject = new {Valor = arg como List }; que tengo en la rejilla de propiedades: Valor = (Colección) [...] con un botón que me muestre la lista de entidades pizca de sus propiedades. –

+0

Entonces, necesito lanzar el objeto deserializado en tiempo de ejecución. El activador y otras cosas devuelven un objeto, entonces no tengo lo que necesito. –

+0

¿Por qué crees que necesitas lanzar? PropertyGrid no se preocupa por su tipo de variable ... –

2

Si el serializador que está utilizando no retiene el tipo - por lo menos, debe almacenar el tipo de de T junto con los datos, y usar eso para crear la lista genérica reflexivamente:

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

Ahora usted tiene listType, que es exactamente el List<T> ha utilizado (por ejemplo, List<Foo>). Puede pasar ese tipo a su rutina de deserialización.

+0

¡Sí! este es un buen enfoque, todavía no tengo la solución.Tengo el objeto deserializado, y tengo el tipo genérico (Lista ). Ahora, necesito convertir el objeto en tiempo de ejecución, lo que necesito hacer algo así como MyTypedObjectArg = objArg como List ; Por qué .. Si Ato la rejilla de propiedades al objeto consigo: Valor = [System.Collection.Generic.List'1], pero si fijo mecanografié objeto Tengo una colección y puedo ver entidades interior y el propiedades dentro de las entidades, etc. –

+1

'typeof (List <>). MakeGenericType (new Type [] {elementType});' resuelto para mí – maiconmm

Cuestiones relacionadas