简体   繁体   中英

ShouldSerializeXXX() Dynamic Implementation


I'm making a form application with a 'propertygrid control.'
I want all properties to always display with non-bold text --- always treat all properties as a 'default' value. And this 'Test' class has a lot of properties.
I think the best way is to dynamically implement 'ShouldSerializeXXX' methods. Manually implementation is lacking in flexibility and not smart.

I know the way to dynamically implement a function using 'DynamicMethod class' [https://docs.microsoft.com/en-US/dotnet/api/system.reflection.emit.dynamicmethod?view=net-5.0]. But the 'ShouldSerializeXXX' function has an effect just by defining, I have no idea how to implement the function.
Can anybody tell me the way to do this? Sorry for my poor English.
 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; }... }

Dynamically implementing a method that needs to then be discovered by reflection is not trivial, and would require dynamically creating a sub-type of Test and making sure that all your instances are actually of the sub-type - not an appealing proposition.

There are some more appealing options, though;

  1. generate the extra methods - this could be a few lines of throw-away reflection code (and a unit test that checks that they all exist) that just spits out however-many versions of public bool ShouldSerialize{Name}() => false;
  2. look into the type-descriptor/property-descriptor API; a custom type-description-provider can provide custom property-descriptors, and it is they that get to say whether something needs to be serialized or not; it is just that the default implementation looks for ShouldSerialize{Name}()

Honestly, option 1 seems by far the easiest option here - and it will involve less mess at runtime; you could get option 1 implemented in a few minutes, including the test to make sure you don't miss any new ones

The basic idea is using a custom type descriptor as it's already addressed in Marc's answer. You can see an implementation in my post here . You can make the linked post working for you easily by changing override of ShouldSerializeValue and return false. That's all.

But here I'd like to share another option, a shorter answer, which needs less effort but basically do the same for you. Using a Proxy when passing objects to PropertyGrid:

Assuming you have a common class like this:

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

This is how you use the proxy:

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

And here is the result after changing properties:

在此处输入图像描述

This is the ObjectProxy which is a class derived from CustomTypeDescriptor will do the magic for you. And here's the class:

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM