定制,复杂,动态reflection解决方案 – C#

我有很多我正在使用的自定义类,我将解释并发布示例。 在解释他们所做的一切之后,我将尝试清楚地描述我的错误发生的条件。

首先,我使用PropertyGrid来显示几种不同类型对象的属性。 因为PropertyGrid的默认绑定不是我想要的描述,我创建了一些自定义类,我将其称为“显示”类。 这些Display类是通过传入一个对象然后创建属性来构造的,这些属性返回格式良好的字符串以及传入的真实对象的公共属性(在某些情况下是方法)的描述。

我将用一些简短的示例代码演示这个:

以下是我想在PropertyGrid中显示的对象示例:

public class Joint { public Joint(...) {...} //properties public string Name { get; set;} public CustomObject CC { get; set;} public List Custom List { get; set;} } 

字符串属性“Name”在PropertyGrid中显示正常但是CustomObject和List没有以对用户友好的方式显示。

所以我尝试通过编写这个类来创建一个解决方案:

 public class DisplayJoint { private Joint _jnt; public DisplayJoint(Joint jnt) { _jnt = jnt; } //properties public string Name { get { return _jnt.Name; } } [TypeConverterAttribute(typeof(ExpandableObjectConverter))] public DisplayCustomObject CC { get { return new DisplayCustomObject(_jnt.CC); } } [TypeConverterAttribute(typeof(ExpandableObjectConverter))] public List CustomList { get; set;} } 

正如您在上面的代码中看到的,我为Joint类和CustomObject类创建了特殊的DisplayClasses。 在我的项目中,我有许多不同类型的对象需要相同类型的重叠显示类属性。

在上面你可以看到我在最后两个属性上面添加的行

[TypeConverterAttribute(typeof运算(ExpandableObjectConverter))]

这行解决了我在propertiesGrid中显示CustomObject的问题(几乎……后面会详细介绍)。 但是,它对我的​​自定义列表属性的工作方式不同。 在自定义列表中,它展开以仅显示计数和容量(列表的实际属性)这是有道理的,为什么这是,但它不是我想要的。 我想在列表中看到实际包含的对象。

在此处输入图像描述

所以这是我复杂的解决方案,最初来自这个问题 :

我有两个类,我用它以属性的forms动态地将对象添加到propertyGrid绑定列表。 第一个(CustomClass)可以在这里下载 。 它用于动态创建属性。 我正在使用的第二个类(DisplayIEnumerable)是从第一个派生的,可以在这里找到。

DisplayIEnumerable类循环遍历列表对象,并使用每个对象中包含的信息向自身添加属性。 传入DisplayClass以准确定义如何在Grid中表示这些对象属性。

到目前为止,一切都很好! 如图所示(图片不是使用提供的类创建的,字符串在我使用的类中的格式不同,删除格式代码以帮助您专注于相关代码:

在此处输入图像描述

现在经过那么长的介绍,真正的问题。 使用上面的技术,我想编写一个可以动态处理我没有为其编写唯一显示类的CustomObjects的类。 我打算为那些使用应用程序进行测试的人留下这些代码,以便他们可以更有效地测试,而无需为我公司的每个CustomObjects都有一个完整的Display Class。 (有数百个)相反,通过将propertyGrid与下面的类绑定,我希望所有属性都是列表和CustomObjects确实有相应的DisplayClasses绑定在它们的位置。

这是我已经尝试过并且有错误的类。 我还没有尝试用我的DisplayIEnumerable类实现Lists的替换,我想先让基本function工作:

 using System; using System.ComponentModel; using System.Collections.Generic; using System.Reflection; using System.Collections; using System.Windows.Forms; internal class DisplayObject : CustomClass { #region Variables protected T _obj; #endregion #region Constructor public DisplayObject(T obj) { if (obj != null) { try { Type currentType = typeof(T); foreach (PropertyInfo propertyInfo in currentType.GetProperties()) { Attribute[] attributes = new Attribute[1]; if (propertyInfo.GetType() is IEnumerable) attributes[0] = new TypeConverterAttribute(typeof(ExpandableObjectConverter)); else attributes[0] = null; this.Add(new CustomProperty(propertyInfo.Name, propertyInfo, propertyInfo.GetType(), false, true, attributes)); } } catch { MessageBox.Show("Failure!"); } } } #endregion #region Properties [Browsable(false)] public object Item { get { return _obj; } set { _obj = value; } } #endregion } 

运行时,PropertyGrid应显示为: 之前

但是,一旦您单击展开箭头,没有任何反应,箭头消失: 后

上面的类有什么问题,我的DisplayIEnumerable类没有错,导致这种行为差异?

我正在使用像这样的DisplayObject类(在DisplayClass中):

  [TypeConverterAttribute(typeof(ExpandableObjectConverter))] public DisplayObject EndJoint { get { if (_member.bcEnd != null) { return new DisplayObject(_member.EndJoint); } else return null; } } 

提前致谢! 如果有人通过这个问题,我会非常感动。

您不必创建特殊类来使用属性网格。 只需使用适当的属性修饰属性即可。 这是一个例子:

两个自定义类:

 public class MyObjType1 { public int Id { get; set; } public string Name { get; set; } public override string ToString() { return Name; } } public class MyObjType2 { public string Reference { get; set; } public override string ToString() { return Reference; } } 

请注意, ToString被覆盖,如果没有为给定类型定义TypeConverter,则默认情况下属性网格使用该属性。

一个“holder”类,包含一组自定义对象:

 public class MyHolder { public MyHolder() { Objects = new List(); } public string Name { get; set; } [TypeConverter(typeof(MyCollectionConverter))] public List Objects { get; private set; } } 

请注意,自定义TypeConverter直接应用于Objects属性。 这是来源:

 public class MyCollectionConverter : ExpandableObjectConverter { public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { IEnumerable enumerable = value as IEnumerable; if (enumerable == null) return base.GetProperties(context, value, attributes); int i = 0; List list = new List(); foreach (object obj in enumerable) { MyItemPropertyDescriptor index = new MyItemPropertyDescriptor(i.ToString(), obj); list.Add(index); i++; } return new PropertyDescriptorCollection(list.ToArray()); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType != typeof(string)) return base.ConvertTo(context, culture, value, destinationType); IEnumerable enumerable = value as IEnumerable; if (enumerable == null) return base.ConvertTo(context, culture, value, destinationType); StringBuilder sb = new StringBuilder(); foreach (object obj in enumerable) { if (sb.Length > 0) { sb.Append(','); } sb.AppendFormat("{0}", obj); } return sb.ToString(); } } 

注意我们覆盖ConvertTo并为其提供一个特殊字符串,该字符串在列表中显示以逗号分隔的对象列表。 GetProperties也被覆盖并使用特殊的PropertyDescriptor ; 它为子对象添加了一个ExpandableObjectConverter属性,因此它们也可以展开:

 public class MyItemPropertyDescriptor : PropertyDescriptor { private object _value; public MyItemPropertyDescriptor(string name, object value) : base(name, new[] { new TypeConverterAttribute(typeof(ExpandableObjectConverter)) }) { _value = value; } public override bool IsReadOnly { get { return false; } } public override object GetValue(object component) { return _value; } public override Type PropertyType { get { return _value == null ? typeof(object) : _value.GetType(); } } public override bool ShouldSerializeValue(object component) { return false; } public override Type ComponentType { get { return typeof(object); } } public override bool CanResetValue(object component) { return false; } public override void ResetValue(object component) { } public override void SetValue(object component, object value) { } } 

现在,这是一些示例代码:

 public partial class Form1 : Form { public Form1() { InitializeComponent(); MyHolder holder = new MyHolder(); for (int i = 0; i < 3; i++) { holder.Objects.Add(new MyObjType1 { Id = i, Name = i + "Name" }); } for (int i = 0; i < 3; i++) { holder.Objects.Add(new MyObjType2 { Reference = "Ref" + i }); } propertyGrid1.SelectedObject = holder; } } 

结果如下:

在此处输入图像描述

自己与TypeConverters合作后,我可以确认它们是底层部分的主要痛苦。 你得到关于什么是实际出错的nada信息,只有奇怪的输出……

Idk如果它有帮助,但也许是一个问题,你添加一个空(null)数组到任何不是 IEnumerable的数组? 尝试将add指令移动到if(…)的范围内。 我不认为这有任何伤害。

另外,你确定(在最后一个使用EndJoint的例子中)getter不返回空指针吗? 空白的条目闻起来像我的经验传递的空指针。