从Reference Source for the ConfigurationProperty class来看,这或许不是bug,而是一个特性。
这里是相关的内部方法,InitDefaultValueFromTypeInfo(我做了一些小的格式修改):
private void InitDefaultValueFromTypeInfo(ConfigurationPropertyAttribute attribProperty,
DefaultValueAttribute attribStdDefault) {
object defaultValue = attribProperty.DefaultValue;
// If there is no default value there - try the other attribute ( the clr standard one )
if ((defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) &&
(attribStdDefault != null)) {
defaultValue = attribStdDefault.Value;
}
// If there was a default value in the prop attribute - check if we need to convert it from string
if ((defaultValue != null) && (defaultValue is string) && (_type != typeof(string))) {
// Use the converter to parse this property default value
try {
defaultValue = Converter.ConvertFromInvariantString((string)defaultValue);
}
catch (Exception ex) {
throw new ConfigurationErrorsException(SR.GetString(SR.Default_value_conversion_error_from_string, _name, ex.Message));
}
}
if (defaultValue == null || defaultValue == ConfigurationElement.s_nullPropertyValue) {
if (_type == typeof(string)) {
defaultValue = String.Empty;
}
else if (_type.IsValueType) {
defaultValue = TypeUtil.CreateInstanceWithReflectionPermission(_type);
}
}
SetDefaultValue(defaultValue);
}
最后一个if 块很有趣:如果您的属性类型为string,并且默认值为null,那么默认值将更改为string.Empty。
第一个if 块暗示了对这种特殊行为的可能解释。 [ConfigurationProperty] 属性的 DefaultValue 属性是可选的。如果程序员未设置DefaultValue,则默认为null。第一个if 块使用默认的null 来检查是否指定了DefaultValue。如果没有,它会退回到从 [DefaultValue] 属性中获取默认值(如果存在)。
这一切都意味着:指定DefaultValue = null 与根本不指定它具有相同的效果,在这种情况下,配置子系统会为字符串选择一个“正常”的默认值:空字符串。
解决方法:
这是一个有点老套的解决方法:不要将您的配置属性声明为string,而是将其声明为字符串周围的薄包装类型;然后声明一个合适的类型转换器:
[ConfigurationProperty("name", IsRequired = false)]
[TypeConverter(typeof(IncognitoStringConverter))] // note: additional attribute!
public IncognitoString Name // note: different property type
{
get
{
return (IncognitoString)base["name"];
}
set
{
base["name"] = value;
}
}
这里是IncognitoString 和IncognitoStringConverter 的实现:
public struct IncognitoString
{
private IncognitoString(string value)
{
this.value = value;
}
private readonly string value;
public static implicit operator IncognitoString(string value)
{
return new IncognitoString(value);
}
public static implicit operator string(IncognitoString incognitoString)
{
return incognitoString.value;
}
… // perhaps override ToString, GetHashCode, and Equals as well.
}
public sealed class IncognitoStringConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return (IncognitoString)(string)value;
}
}
因为IncognitoString 可以隐式转换为string,所以您可以将属性值分配给任何字符串变量。我知道,仅仅为了获得可为空的属性就很麻烦而且真的很复杂。也许只是忍受空字符串。