ShouldSerializeXXX() 动态实现

问题描述


我正在制作一个带有“propertygrid 控件”的表单应用程序。
我希望所有属性始终以非粗体文本显示 --- 始终将所有属性视为“认”值。这个“测试”类有很多属性
我认为最好的方法是动态实现 'ShouldSerializeXXX' 方法。手动实现缺乏灵活性,不智能。

我知道使用“Dynamicmethod 类”动态实现函数方法[https://docs.microsoft.com/en-US/dotnet/api/system.reflection.emit.dynamicmethod?view=net-5.0]。但是'ShouldSerializeXXX'函数只是定义就起作用了,我不知道如何实现该函数
谁能告诉我如何做到这一点?对不起,我的英语不好。
public class Test
{
      public int AAA {get;set;}
      public string BBB {get;set;}
      public bool CCC {get;set;}
      ...
      ...

      //This class has a lot of property,so I want to dynamically implement the function like this:
      private bool ShouldSerializeAAA(){ return false; }
      private bool ShouldSerializeBBB(){ return false; }
      ...
}

解决方法

动态实现一个方法然后需要被反射发现不是微不足道的,并且需要动态创建 Test 的子类型并确保您的所有实例实际上是子类型 - 不是一个吸引人的命题。

不过,还有一些更吸引人的选择;

  1. 生成额外的方法——这可能是几行丢弃的反射代码(以及一个检查它们是否都存在的单元测试),它只是吐出很多版本的{{ 1}}
  2. 查看 type-descriptor/property-descriptor API;自定义类型描述提供程序可以提供自定义属性描述符,并且他们可以决定某些内容是否需要序列化;只是 default 实现寻找 public bool ShouldSerialize{Name}() => false;

老实说,选项 1 在这里似乎是最简单的选项 - 它在运行时不会造成混乱;您可以在几分钟内实施选项 1,包括测试以确保您不会错过任何新测试

,

基本思想是使用自定义类型描述符,因为它已在 Marc 的回答中解决。您可以在我的帖子 here 中看到一个实现。您可以通过更改 ShouldSerializeValue 的覆盖并返回 false 来轻松地使链接的帖子为您工作。仅此而已。

但在这里我想分享另一种选择,一个更短的答案,它需要更少的努力,但基本上为你做同样的事情。将对象传递给 PropertyGrid 时使用代理:

假设你有一个这样的公共类:

public class MyClass
{
    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
}

这是您使用代理的方式:

var myOriginalObject = new MyClass();
this.propertyGrid1.SelectedObject = new ObjectProxy(myOriginalObject);

这是更改属性后的结果:

enter image description here

这是 ObjectProxy,它是从 CustomTypeDescriptor 派生的类,将为您带来神奇的效果。这是课程:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
public class ObjectProxy : CustomTypeDescriptor
{
    public object Original { get; private set; }
    public List<string> BrowsableProperties { get; private set; }
    public ObjectProxy(object o)
        : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o)) => Original = o;
    public override PropertyDescriptorCollection GetProperties(Attribute[] a)
    {
        var props = base.GetProperties(a).Cast<PropertyDescriptor>()
            .Select(p => new MyPropertyDescriptor(p));
        return new PropertyDescriptorCollection(props.ToArray());
    }
    public override object GetPropertyOwner(PropertyDescriptor pd) => Original;
}
public class MyPropertyDescriptor : PropertyDescriptor
{
    PropertyDescriptor o;
    public MyPropertyDescriptor(PropertyDescriptor originalProperty)
        : base(originalProperty) => o = originalProperty;
    public override bool CanResetValue(object c) => o.CanResetValue(c);
    public override object GetValue(object c) => o.GetValue(c);
    public override void ResetValue(object c) => o.ResetValue(c);
    public override void SetValue(object c,object v) => o.SetValue(c,v);
    public override bool ShouldSerializeValue(object c) => false;
    public override AttributeCollection Attributes => o.Attributes;
    public override Type ComponentType => o.ComponentType;
    public override bool IsReadOnly => o.IsReadOnly;
    public override Type PropertyType => o.PropertyType;
}