2009-11-26 5 views
5

Aus auditiven Gründen speichere ich die Argumente der Business-Methoden serialisiert in der Datenbank mit dem binaryformatter.C# - Wie man eine generische Liste deserialisiert <T>, wenn ich den Typ von (T) nicht kenne?

Das Problem ist, dass, wenn ein Argument eine generische Liste ist ich finde nicht die Möglichkeit, das deserialized Objekt zu werfen, weil ich den Typ nicht kenne, oder wenn ich den Typ weiß ich nicht wissen, wie Wirf das Objekt zur Laufzeit.

Jeder weiß, wie man ein Objekt, das eine generische Liste enthält, zur Laufzeit dynamisch darstellt.

Ich muss dies tun, weil ich die deserialisiert Daten in einem Eigenschaftenraster zeigen müssen:

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

"Auditorische Gründe"? Was bedeutet das in diesem Zusammenhang? –

+0

Für die Einhaltung der Auditvorschriften würde ich mir vorstellen. – AakashM

+0

Für Informationen, 'BinaryFormatter' ist ziemlich spröde, wenn Sie Ihre Objekte ändern. Ich würde einen contract-based Serializer wie XmlSerializer, DataContractSerializer oder (für binäres) protobuf-net empfehlen. –

Antwort

3

Mit BinaryFormatter Sie nicht benötigen, um den Typ zu kennen; Die Metadaten sind im Stream enthalten (größer, aber hey!). Sie können jedoch nicht Cast, wenn Sie den Typ kennen. In diesem Szenario müssen Sie häufig bekannte Schnittstellen (nicht-generische IList usw.) und Reflektionen verwenden. Und viel davon.

Ich kann auch nicht an eine große Anforderung zu wissen, den Typ in einer PropertyGrid zu zeigen - da dies object akzeptiert, geben Sie es einfach, was BinaryFormatter bietet. Gibt es ein bestimmtes Problem, das Sie dort sehen? Vielleicht möchten Sie erneut nach IList (nicht-generisch) suchen - aber es lohnt sich nicht, sich um IList<T> zu kümmern, da dies nicht das ist, was PropertyGrid prüft!

Sie können natürlich die T finden, wenn Sie (like so) wollen - und verwenden MakeGenericType() und Activator.CreateInstance - nicht schön.


OK; Hier ist ein Weg mit benutzerdefinierten Deskriptoren, die nicht beinhalten Wissen irgendetwas über das Objekt oder den Listentyp; wenn Sie wirklich wollen, ist es möglich, die Listenelemente direkt in die Eigenschaften zu erweitern, so dass in diesem Beispiel Sie 2 gefälschte Eigenschaften sehen würden („Fred“ und „Wilma“) - die zusätzliche Arbeit, wenn auch ;-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

Hallo Marc, ich weiß nicht warum, aber wenn ich das Eigenschaftenraster like dies: pgArgsIn.SelectedObject = neu {Value = arg}; // Wenn arg das Deserialisierungsargument ist. Ich habe so etwas in der Eigenschaft Raster: Wert = [system.collections ....], ohne etwas mehr, das bringt mir eine Chance zu wissen, was das Objekt in sich haben. Aber, wenn ich das Eigenschaftenraster wie folgt setzen: pgArgsIn.SelectedObject = new {Value = arg als Liste }; Ich habe in der Eigenschaft Raster: Wert = (Sammlung) [...] mit einer Schaltfläche, die mir die Liste der Entitäten mit ihren Eigenschaften zeigen. –

+0

Dann muss ich das deserialized Objekt zur Laufzeit casten. Der Aktivator und andere Dinge geben ein Objekt zurück, dann habe ich nicht das, was ich brauche. –

+0

Warum denkst du, du musst wirken? PropertyGrid interessiert sich nicht für Ihren Variablentyp ... –

2

Wenn die Serializer Sie verwenden nicht die Art behalten - zumindest, müssen Sie den Typ speichern mit den Daten von T zusammen, und verwenden Sie die generische Liste erstellen reflektiv:

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

Jetzt haben Sie listType, die die genaue List<T> Sie verwendet (etwa List<Foo>). Sie können diesen Typ in Ihre Deserialisierungsroutine übernehmen.

+0

betrifft Ja! das ist ein guter Ansatz, ich habe noch keine Lösung.Ich habe das deserialisierte Objekt, und ich habe den generischen Typ (Liste ). Jetzt muss ich das Objekt zur Laufzeit umwandeln. Ich muss etwas wie MyTypedObjectArg = objArg als List ; Warum? .. Wenn ich das Eigenschaftenraster an das Objekt binden, bekomme ich: Value = [System.Collection.Generic.List'1], aber wenn ich ein getipptes Objekt setze habe ich eine Sammlung und ich kann die Entitäten innerhalb und die Eigenschaften innerhalb der Entitäten usw. –

+1

'typeof (Liste <>). MakeGenericType (neuer Typ [] {elementType});' gelöst für mich – maiconmm

Verwandte Themen