Ответ 1
Судя по Reference Source для класса ConfigurationProperty
, это, возможно, не ошибка, а особенность.
Вот соответствующий внутренний метод 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
, вы можете присвоить значение свойства любой строковой переменной. Я знаю, он взломан и очень сложный, чтобы получить свойства с нулевым значением. Возможно, просто живите с пустой строкой.