问题描述
我有一个自定义的可编辑 ConfigurationElement,通常我将它用作一些类的设置对象。 (通过构造函数传递)
当我从具有自己的设置属性的外部 dll 中获得一个对象时(因此我无法将其更改为直接从我的配置元素读取),我正在使用此扩展将配置属性值复制到对象属性:
/// <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,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 上为此创建一个事件?
附言我不想使用 INotifyPropertyChanged 接口,因为在每个属性中添加调用会非常麻烦。 我问因为 ConfigurationElement 有它的索引器,所以也许在这个基类上有一种我不知道的方法。
解决方法
我发现 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);
}