c# - كيفية إلغاء تسلسل قائمة عامة<T> عندما لا أعرف نوع (T)؟

StackOverflow https://stackoverflow.com/questions/1804859

سؤال

لأسباب سمعية، أقوم بتخزين وسيطات أساليب العمل المتسلسلة في قاعدة البيانات باستخدام المنسق الثنائي.

المشكلة هي أنه عندما تكون الوسيطة عبارة عن قائمة عامة، لا أجد طريقة لإلقاء الكائن الذي تم إلغاء تسلسله لأنني لا أعرف النوع، أو إذا كنت سأعرف النوع فلا أعرف كيفية إلقاء الكائن عليه مدة العرض.

هل يعرف أي شخص كيفية إرسال كائن يحتوي على قائمة عامة ديناميكيًا في وقت التشغيل؟

أحتاج إلى القيام بذلك لأنني بحاجة إلى إظهار البيانات التي تم إلغاء تسلسلها في شبكة الخصائص:

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 لم تكن يحتاج لمعرفة النوع؛يتم تضمين البيانات الوصفية في الدفق (مما يجعلها أكبر، ولكن مهلا!).ومع ذلك، لا يمكنك ذلك يقذف إلا إذا كنت تعرف النوع.في كثير من الأحيان في هذا السيناريو، يتعين عليك استخدام واجهات معروفة شائعة (غير عامة IList الخ) والتأمل.والكثير منه.

لا يمكنني أيضًا التفكير في متطلب كبير لمعرفة النوع الذي سيتم عرضه في ملف PropertyGrid - وبما أن هذا يقبل object, ، فقط أعطه ما BinaryFormatter يوفر.هل هناك مشكلة محددة تراها هناك؟مرة أخرى، قد ترغب في التحقق من ذلك IList (غير عام) - لكن الأمر لا يستحق القلق IList<T>, ، لأن هذا ليس ما PropertyGrid الشيكات ل!

يمكنك بالطبع العثور على T اذا أنت يريد (مثل ذلك) - والاستخدام MakeGenericType() و Activator.CreateInstance - ليست جميلة.


نعم؛إليك طريقة لاستخدام الواصفات المخصصة التي لا تنطوي على معرفة أي شئ حول الكائن أو نوع القائمة؛اذا أنت حقًا تريد أنه من الممكن توسيع عناصر القائمة مباشرة إلى الخصائص، لذا في هذا المثال ستشاهد خاصيتين مزيفتين ("فريد" و"ويلما") - وهذا عمل إضافي، بالرغم من ذلك؛-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;
            }
        }
    }
}

نصائح أخرى

وإذا كان مسلسل الذي تستخدمه لا تحتفظ نوع - على الأقل، يجب تخزين نوع 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<T> الدقيق الذي استخدمته (ويقول، List<Foo>). يمكنك تمرير هذا النوع إلى روتين إلغاء التسلسل الخاص بك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top