Получение коллекции сконструированных подклассов с использованием отражения - PullRequest
0 голосов
/ 25 сентября 2008

Я хочу создать класс, который реализует IEnumerable , но, используя отражение, генерирует T и возвращает их через IEnumerable , где T 'является полностью сконструированным подклассом T, некоторые свойства которого скрыты, а другие доступны только для чтения. ,

Хорошо, это может быть не очень понятно. Позвольте мне объяснить это с помощью кода - я хотел бы иметь класс CollectionView следующим образом: -

public class CollectionView<T> : IEnumerable<T> {
  public CollectionView(IEnumerable<T> inputCollection, 
    List<string> hiddenProperties, List<string> readonlyProperties) {
    // ...
  }

  // IEnumerable<T> implementation which returns a collection of T' where T':T.
}

...

public class SomeObject {
  public A { get; set; }
  public B { get; set; }
  public C { get; set; }
}

...

var hiddenProperties   = new List<string>(new[] { "A" });
var readOnlyProperties = new List<string>(new[] { "C" });

IEnumerable<SomeObject> someObjects = CollectionView<SomeObject>(hiddenProperties,
  readOnlyProperties);

...

dataGridView1.DataSource = someObjects;

(При отображении в dataGridView1 отображаются столбцы B и C, а C имеет базовое хранилище, доступное только для чтения)

Возможно ли это / желательно или я полностью сошел с ума / этот вопрос свидетельствует о моей глубокой неадекватности как программиста?

Я хочу сделать это, чтобы я мог манипулировать коллекцией, которая должна быть передана в DataGridView, без необходимости непосредственно манипулировать DataGridView, чтобы скрыть столбцы / сделать столбцы доступными только для чтения . Так что нет, просто используйте dataGridView1.Columns.Remove (blah) / dataGridView1.Columns [blah] .ReadOnly = true ', пожалуйста, ответьте !!

Помощь!

Ответы [ 4 ]

1 голос
/ 25 сентября 2008

Castle.DynamicProxy поможет вам в этом. Что бы вы сделали, это создали перехватчик, который наследовал бы T. Вы бы хранили коллекцию скрытых и доступных только для чтения свойств. Когда вызывается метод получения или установки, перехватчик проверяет, существует ли свойство в какой-либо коллекции, и затем предпринимает соответствующие действия.

Однако я не знаю, как вы могли бы скрыть собственность. Вы не можете изменить модификатор доступа базового класса в производном классе. Вы МОЖЕТЕ использовать ключевое слово new, но я не знаю, как это сделать с Castle.DynamicProxy.

0 голосов
/ 19 октября 2012

Вы также можете использовать ICustomTypeDescriptor для фильтрации списка свойств. Для этого я создал класс-оболочку для объекта данных (MyWrapper), дескриптор пользовательского свойства (MypropertyDescriptor) и класс коллекции. Я расширил класс коллекции, чтобы также наблюдать IList, чтобы данные могли быть изменены, и ITypedList, чтобы сетка данных могла строить столбцы. Возможно, вы также захотите наследовать ObservableCollection <> или BindingList <>.

Пользовательский дескриптор должен обрабатывать установку и получение значения свойства:

public sealed class MyPropertyDescriptor : System.ComponentModel.PropertyDescriptor
{
    private PropertyDescriptor innerProperty;
    private Boolean isReadonly;

    public MyPropertyDescriptor(PropertyDescriptor innerProperty, Boolean isReadonly)
        : base(innerProperty.Name, GetAttributeArray(innerProperty.Attributes))
    {
        this.innerProperty = innerProperty;
        this.isReadonly = isReadonly;
        if (!isReadonly) this.isReadonly = innerProperty.IsReadOnly;
    }

    public override Type ComponentType
    {
        get { return this.innerProperty.ComponentType; }
    }
    public override Boolean IsReadOnly
    {
        get { return this.isReadonly; }
    }
    public override Type PropertyType
    {
        get { return this.innerProperty.PropertyType; }
    }
    public override String Name
    {
        get
        {
            return this.innerProperty.Name;
        }
    }
    public override String DisplayName
    {
        get
        {
            return this.innerProperty.DisplayName;
        }
    }
    public override Boolean SupportsChangeEvents
    {
        get
        {
            return true;
        }
    }
    public override void SetValue(Object component, Object value)
    {
        if (!this.isReadonly)
        {
            this.innerProperty.SetValue(component, value);
            if (component is MyWrapper) (component as MyWrapper).NotifyPropertyChanged(this.innerProperty.Name);
        }
    }
    public override Object GetValue(Object component)
    {
        return this.innerProperty.GetValue(component);
    }

    public override Boolean CanResetValue(Object component)
    {
        return false;
    }
    public override void ResetValue(Object component)
    {
    }
    public override Boolean ShouldSerializeValue(Object component)
    {
        return true;
    }

    private static Attribute[] GetAttributeArray(AttributeCollection attributes)
    {
        List<Attribute> attr = new List<Attribute>();
        foreach (Attribute a in attributes) attr.Add(a);

        return attr.ToArray();
    }
}

Класс-обертка для управления доступом к свойствам через ICustomTypeDescriptor:

public sealed class MyWrapper : System.ComponentModel.ICustomTypeDescriptor, System.ComponentModel.INotifyPropertyChanged
{
    private Object innerObject;
    private String[] hiddenProps;
    private String[] readonlyProps;

    private Type innerType;

    public MyWrapper(Object innerObject, String[] hiddenProps, String[] readonlyProps)
        : base()
    {
        this.innerObject = innerObject;
        this.hiddenProps = hiddenProps;
        this.readonlyProps = readonlyProps;
        this.innerType = innerObject.GetType();
    }

    public static PropertyDescriptorCollection FilterProperties(PropertyDescriptorCollection pdc, String[] hiddenProps, String[] readonlyProps)
    {
        List<PropertyDescriptor> list = new List<PropertyDescriptor>();

        foreach (PropertyDescriptor pd in pdc)
        {
            if (hiddenProps != null)
            {
                Boolean isHidden = false;
                foreach (String hidden in hiddenProps)
                {
                    if (hidden.Equals(pd.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        isHidden = true;
                        break;
                    }
                }
                if (isHidden) continue; // skip hidden
            }

            Boolean isReadonly = false;
            if (readonlyProps != null)
            {
                foreach (String rp in readonlyProps)
                {
                    if (rp.Equals(pd.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        isReadonly = true;
                        break;
                    }
                }
            }

            list.Add(new MyPropertyDescriptor(pd, isReadonly));
        }

        return new PropertyDescriptorCollection(list.ToArray());
    }

    #region ICustomTypeDescriptor Members

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
    {
        return FilterProperties(TypeDescriptor.GetProperties(this.innerType, attributes), hiddenProps, readonlyProps);
    }
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
    {
        return FilterProperties(TypeDescriptor.GetProperties(this.innerType), hiddenProps, readonlyProps);
    }

    AttributeCollection ICustomTypeDescriptor.GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this.innerType);
    }

    String ICustomTypeDescriptor.GetClassName()
    {
        return TypeDescriptor.GetClassName(this.GetType());
    }
    String ICustomTypeDescriptor.GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this.GetType());
    }
    TypeConverter ICustomTypeDescriptor.GetConverter()
    {
        return TypeDescriptor.GetConverter(this.GetType());
    }
    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this.GetType());
    }
    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
    {
        return null;
    }
    Object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this.GetType(), editorBaseType);
    }
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
    {
        return null;
    }
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
        return null;
    }

    Object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
    {
        return this.innerObject;
    }

    #endregion

    #region INotifyPropertyChanged Members
    internal void NotifyPropertyChanged(String propertyName)
    {
        if (this.propertyChanged != null) this.propertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    private event PropertyChangedEventHandler propertyChanged;
    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
    {
        add { propertyChanged += value; }
        remove { propertyChanged -= value; }
    }
    #endregion
}

И модифицированная версия вашего CollectionView <>. Большая часть этого примера просто отображает методы интерфейса во внутренний список.

public sealed class CollectionView<T> : IEnumerable<MyWrapper>, System.Collections.IList, IList<MyWrapper>, ITypedList
{
    private String[] hiddenProps;
    private String[] readonlyProps;
    private List<MyWrapper> collection;

    public CollectionView(IEnumerable<T> innerCollection, String[] hiddenProps, String[] readonlyProps)
        : base()
    {
        this.hiddenProps = hiddenProps;
        this.readonlyProps = readonlyProps;

        this.collection = new List<MyWrapper>();
        foreach (T item in innerCollection)
        {
            this.collection.Add(new MyWrapper(item, hiddenProps, readonlyProps));
        }
    }

    #region ITypedList Members

    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        return MyWrapper.FilterProperties(TypeDescriptor.GetProperties(typeof(T)), this.hiddenProps, this.readonlyProps);
    }

    String ITypedList.GetListName(PropertyDescriptor[] listAccessors)
    {
        return null;
    }
    #endregion

    #region IEnumerable<MyWrapper> Members

    IEnumerator<MyWrapper> IEnumerable<MyWrapper>.GetEnumerator()
    {
        return this.collection.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.collection.GetEnumerator();
    }

    #endregion

    #region IList Members
    Int32 System.Collections.IList.Add(Object value)
    {
        return (this.collection as System.Collections.IList).Add(value);
    }
    void System.Collections.IList.Clear()
    {
        (this.collection as System.Collections.IList).Clear();
    }
    Boolean System.Collections.IList.Contains(Object value)
    {
        return (this.collection as System.Collections.IList).Contains(value);
    }
    Int32 System.Collections.IList.IndexOf(Object value)
    {
        return (this.collection as System.Collections.IList).IndexOf(value);
    }
    void System.Collections.IList.Insert(Int32 index, Object value)
    {
        (this.collection as System.Collections.IList).Insert(index, value);
    }
    Boolean System.Collections.IList.IsFixedSize
    {
        get { return (this.collection as System.Collections.IList).IsFixedSize; }
    }
    Boolean System.Collections.IList.IsReadOnly
    {
        get { return (this.collection as System.Collections.IList).IsReadOnly; }
    }
    void System.Collections.IList.Remove(Object value)
    {
        (this.collection as System.Collections.IList).Remove(value);
    }
    void System.Collections.IList.RemoveAt(Int32 index)
    {
        (this.collection as System.Collections.IList).RemoveAt(index);
    }
    Object System.Collections.IList.this[Int32 index]
    {
        get
        {
            return (this.collection as System.Collections.IList)[index];
        }
        set
        {
            (this.collection as System.Collections.IList)[index] = value;
        }
    }
    #endregion

    #region ICollection Members
    void System.Collections.ICollection.CopyTo(Array array, Int32 index)
    {
        (this.collection as System.Collections.ICollection).CopyTo(array, index);
    }
    Int32 System.Collections.ICollection.Count
    {
        get { return (this.collection as System.Collections.ICollection).Count; }
    }
    Boolean System.Collections.ICollection.IsSynchronized
    {
        get { return (this.collection as System.Collections.ICollection).IsSynchronized; }
    }
    Object System.Collections.ICollection.SyncRoot
    {
        get { return (this.collection as System.Collections.ICollection).SyncRoot; }
    }
    #endregion

    #region IList<MyWrapper> Members
    Int32 IList<MyWrapper>.IndexOf(MyWrapper item)
    {
        return this.collection.IndexOf(item);
    }
    void IList<MyWrapper>.Insert(Int32 index, MyWrapper item)
    {
        this.collection.Insert(index, item);
    }
    void IList<MyWrapper>.RemoveAt(Int32 index)
    {
        this.collection.RemoveAt(index);
    }
    MyWrapper IList<MyWrapper>.this[Int32 index]
    {
        get
        {
            return this.collection[index];
        }
        set
        {
            this.collection[index] = value;
        }
    }
    #endregion

    #region ICollection<MyWrapper> Members
    void ICollection<MyWrapper>.Add(MyWrapper item)
    {
        this.collection.Add(item);
    }
    void ICollection<MyWrapper>.Clear()
    {
        this.collection.Clear();
    }
    Boolean ICollection<MyWrapper>.Contains(MyWrapper item)
    {
        return this.collection.Contains(item);
    }
    void ICollection<MyWrapper>.CopyTo(MyWrapper[] array, Int32 arrayIndex)
    {
        this.collection.CopyTo(array, arrayIndex);
    }
    Int32 ICollection<MyWrapper>.Count
    {
        get { return this.collection.Count; }
    }
    Boolean ICollection<MyWrapper>.IsReadOnly
    {
        get { return false; }
    }
    Boolean ICollection<MyWrapper>.Remove(MyWrapper item)
    {
        return this.collection.Remove(item);
    }
    #endregion
}
0 голосов
/ 26 сентября 2008

Я решил по-другому взглянуть на эту проблему, я действительно не видел дрова для деревьев! Я решил создать метод расширения, который преобразует мой IEnumerable в таблицу данных, которую затем можно передавать по мере необходимости: -

public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
{
    DataTable ret = new DataTable();

    Type type = typeof(T);

    foreach (PropertyInfo propertyInfo in type.GetProperties())
    {
        // Ignore indexed properties.
        if (propertyInfo.GetIndexParameters().Length > 0) continue;
        ret.Columns.Add(propertyInfo.Name);
    }

    foreach (T data in collection)
    {
        DataRow row = ret.NewRow();
        foreach (PropertyInfo propertyInfo in type.GetProperties())
        {
            // Ignore indexed properties.
            if (propertyInfo.GetIndexParameters().Length > 0) continue;

            row[propertyInfo.Name] = propertyInfo.GetValue(data, null);
        }

        ret.Rows.Add(row);
    }

    return ret;
}
0 голосов
/ 25 сентября 2008

Вы просто не можете скрыть свойства, даже создавая подклассы прокси. Вы можете, по крайней мере, создать другой тип динамически, который содержит хорошие свойства, но это не будет T.

Но возврата списка объектов может быть достаточно, если вам просто нужно использовать привязку данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...