使用 PropertyChanged 事件创建自定义 ConfigurationElement

Creating a custom ConfigurationElement with PropertyChanged event

提问人:KBar 提问时间:3/7/2021 更新时间:3/9/2021 访问量:125

问:

我有一个自定义的可编辑 ConfigurationElement,通常我将其用作某些类的设置对象。(通过构造函数传递它)

当我有一个来自外部dll的对象,它有自己的设置属性(所以我不能将其更改为直接从我的config元素读取)时,我正在使用此扩展将配置属性值复制到对象属性:

/// <summary>
/// Set the object properties from a configuration element including the unrecognized attributes.
/// </summary>
/// <typeparam name="T">The object type</typeparam>
/// <param name="obj">The object to set</param>
/// <param name="configElement">The configuration element to take the properties from</param>
/// <returns></returns>
public static T SetProperties<T>(this T obj, BaseConfigurationElement configElement) => obj.SetProperties(configElement.GetProperties(true));

public static T SetProperties<T>(this T obj, object properties) => SetProperties(obj, properties?.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(properties)));
public static T SetProperties<T>(this T obj, Dictionary<string, string> properties) => SetProperties(obj, properties.ToDictionary(i => i.Key, i => i.Value as object));
public static T SetProperties<T>(this T obj, Dictionary<string, object> properties)
{
    if (obj != null && properties != null)
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
            if (properties.Keys.Contains(pi.Name) && pi.CanWrite)
                try // Convert value to property type.
                {
                    object valueToSet = properties[pi.Name];

                    if (pi.PropertyType.IsEnum)
                        pi.SetValue(obj, Enum.Parse(pi.PropertyType, valueToSet.ToString()));
                    else pi.SetValue(obj, Convert.ChangeType(valueToSet, pi.PropertyType), null);
                }
                catch (Exception ex) { Logging.WriteError($"Can't convert from type [{GetTypeName(properties[pi.Name])}] to type [{pi.PropertyType.Name}] for property [{pi.Name}] of object type [{GetTypeName(obj)}]: {ex.Message}"); }

    return obj;
}

关键是我想实时更改配置,但是在更改值时,ConfigurationElement上没有会出现任何事件,因此我可以重新复制更改的属性。

有没有办法在我的自定义 ConfigurationElement 上为此创建事件?

P.s. 我不想使用 INotifyPropertyChanged 接口,因为在每个属性中添加调用会非常麻烦。 我问是因为 ConfigurationElement 有它的索引器,所以也许在这个基类上有一种我不知道的方法。

C# 属性 配置 System.Configuration

评论


答:

0赞 KBar 3/9/2021 #1

我发现受SetPropertyValue保护的方法没有被标记为虚拟,如果它是虚拟的,它可能会更容易。

我的解决方案是使用“new”关键字隐藏索引器,这不像覆盖,但没关系,因为索引器不是公共的,所以在继承自定义类时,您通常不会将自己转换为基类型。

这应该有效:

public class PropertyChangedEventArgs : EventArgs
{
    public ConfigurationProperty Property { get; }
    public object Value { get; }
    public PropertyChangedEventArgs(ConfigurationProperty property, object value) { Property = property; Value = value; }
}

public abstract class BaseConfigurationElement : ConfigurationElement
{
    protected new Object this[ConfigurationProperty prop]
    {
        get => base[prop];
        set
        {
            base[prop] = value;
            OnPropertyChanged(new PropertyChangedEventArgs(prop, value)); // Must be arise only after the base indexer was set, because an exception may will be thrown from the indexer.
        }
    }

    protected new Object this[String propertyName]
    {
        get => base[propertyName];
        set
        {
            base[propertyName] = value;
            OnPropertyChanged(new PropertyChangedEventArgs(Properties[propertyName], value));
        }
    }

    public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
}