0

我想创建一个实现 IEnumerable<T> 的类,但使用反射生成 T 并通过 IEnumerable<T> 返回它们,其中 T' 是 T 的完全构造的子类,其中一些属性隐藏,其他属性只读。

好吧,那可能不是很清楚。让我通过代码来解释这一点 - 我想要一个 CollectionView<T> 类,如下所示: -

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

4 回答 4

1

Castle.DynamicProxy将帮助您完成此任务。您要做的是创建一个继承 T 的拦截器。您将存储隐藏和只读属性的集合。当调用 getter 或 setter 时,拦截器将检查该属性是否存在于任一集合中,然后采取适当的措施。

但是,我不知道您将如何隐藏财产。您不能在派生类中更改基类的访问修饰符。您可以使用new关键字,但我不知道如何使用 Castle.DynamicProxy 来做到这一点。

于 2008-09-25T16:19:30.627 回答
0

我决定对这个问题采取不同的方法,我真的没有只见树木不见森林!我决定创建一个扩展方法,将我的 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;
}
于 2008-09-26T12:18:25.947 回答
0

即使通过创建子类代理,您也无法隐藏属性。您至少可以动态构造一个不同的类型,它具有良好的属性,但它不会是T.

但是,如果您只需要使用数据绑定,则返回一个对象列表就足够了。

于 2008-09-25T16:29:09.637 回答
0

您还可以使用 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
}
于 2012-10-19T17:50:46.360 回答