具有空默认值的配置字符串
Configuration String with Null DefaultValue
我有以下 ConfigurationProperty 作为元素的一部分:
[ConfigurationProperty("example", IsRequired = false, DefaultValue = null)]
public string Example {
get { return (string)base["example"]; }
set { base["example"] = value; }
}
如果我按如下方式设置它,它会采用 "Hello"
字符串并正常工作:
<myElement example="Hello"/>
如果不存在,我运行陷入问题:
<myElement/>
它没有采用上面指定的默认值 null
,而是采用 String.Empty
。为什么会这样,我怎样才能让它采用默认值 null
?
更新
肯定是因为base["example"]
returnsString.Empty
,其中base
是一个ConfigurationElement
(索引器在这里定义:https://msdn.microsoft.com/en-us/library/c8693ks1(v=vs.110).aspx) , 但我仍然不确定为什么它不采用 null
.
的值
更新
甚至 DefaultValue = default(string)
将字符串设置为 String.Empty
。
更新
即使 base.Properties.Contains("example")
returns true
如果 属性 在配置中不存在。
从 Reference Source for the ConfigurationProperty
class 来看,这可能不是一个错误,而是一个功能。
这里是相关的内部方法,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
与根本不指定它具有相同的效果,在这种情况下,配置子系统为字符串选择一个 "sane" 默认值:空字符串.
解决方法:
这里有一个有点老套的解决方法:不要将配置 属性 声明为 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
,您可以将 属性 值分配给任何字符串变量。我知道,获取可为 null 的属性很复杂,而且非常复杂。也许只是接受空字符串。
另一种解决方法是像这样进行调用:
[ConfigurationProperty("Prompt")]
public string Prompt
{
get { return this.GetNullableStringValue("Prompt"); }
}
private string GetNullableStringValue(string propertyName)
{
return (string)this[new ConfigurationProperty(propertyName, typeof(string), null)];
}
像这样调用 GetNullableString
会绕过配置 属性 属性并停止它,将 DefaultValue 默认为 null。您也可以将该方法放在基础 class 中,以使其更整洁。
如果你想更改默认值,你只需要记住你正在调用它。
如果您想取消可能在属性上定义的其他一些内容,您也可以调用 this.ElementInformation.Properties[propertyName]
- 只是不要使用它来填充 DefaultValue
ConfigurationElement
类型有一个 ElementInformation
属性,后者又有一个 IsPresent
属性。
因此,与其尝试 return 空值 ConfigurationElement
,不如检查 IsPresent
属性 以查看 "whether the associated ConfigurationElement object is in the configuration file." 1
例如:
if (Configuration.Example.ElementInformation.IsPresent)
{
...
}
我选择使用更具可读性和可重用性的扩展方法ToNullIfEmpty()
。我保留了 DefaultValue 以防 non-intuitive 行为发生变化,将空字符串转换为 String.Empty.
[ConfigurationProperty("dataCenterRegion", DefaultValue = null)]
public string DataCenterRegion
{
get { return ((string)this["dataCenterRegion"]).ToNullIfEmpty(); }
set { this["dataCenterRegion"] = value; }
}
public static partial class ExtensionMethods
{
/// <summary>
/// Return null if the string is empty or is already null.
/// Otherwise, return the original string.
/// </summary>
public static string ToNullIfEmpty(this string str)
{
return String.IsNullOrEmpty(str) ? null : str;
}
/// <summary>
/// Return null if the string is white space, empty or is already null.
/// Otherwise, return the original string.
/// </summary>
public static string ToNullIfWhiteSpaceOrEmpty(this string str)
{
return String.IsNullOrWhiteSpace(str) ? null : str;
}
}
无需检查 null
的 属性 值,您可以轻松检查 属性 是否已在配置文件中设置或是否已返回默认值。这可以通过查看 ConfigurationElement
的 ElementInformation
.
中的 ValueOrigin
来完成
// if not the default value...
if (MyConfigurationElement.ElementInformation.Properties["example"].ValueOrigin!=
PropertyValueOrigin.Default)
{
...
}
另请参阅 PropertyValueOrigin Enumeration 值的文档。
我有以下 ConfigurationProperty 作为元素的一部分:
[ConfigurationProperty("example", IsRequired = false, DefaultValue = null)]
public string Example {
get { return (string)base["example"]; }
set { base["example"] = value; }
}
如果我按如下方式设置它,它会采用 "Hello"
字符串并正常工作:
<myElement example="Hello"/>
如果不存在,我运行陷入问题:
<myElement/>
它没有采用上面指定的默认值 null
,而是采用 String.Empty
。为什么会这样,我怎样才能让它采用默认值 null
?
更新
肯定是因为base["example"]
returnsString.Empty
,其中base
是一个ConfigurationElement
(索引器在这里定义:https://msdn.microsoft.com/en-us/library/c8693ks1(v=vs.110).aspx) , 但我仍然不确定为什么它不采用 null
.
更新
甚至 DefaultValue = default(string)
将字符串设置为 String.Empty
。
更新
即使 base.Properties.Contains("example")
returns true
如果 属性 在配置中不存在。
从 Reference Source for the ConfigurationProperty
class 来看,这可能不是一个错误,而是一个功能。
这里是相关的内部方法,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
与根本不指定它具有相同的效果,在这种情况下,配置子系统为字符串选择一个 "sane" 默认值:空字符串.
解决方法:
这里有一个有点老套的解决方法:不要将配置 属性 声明为 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
,您可以将 属性 值分配给任何字符串变量。我知道,获取可为 null 的属性很复杂,而且非常复杂。也许只是接受空字符串。
另一种解决方法是像这样进行调用:
[ConfigurationProperty("Prompt")]
public string Prompt
{
get { return this.GetNullableStringValue("Prompt"); }
}
private string GetNullableStringValue(string propertyName)
{
return (string)this[new ConfigurationProperty(propertyName, typeof(string), null)];
}
像这样调用 GetNullableString
会绕过配置 属性 属性并停止它,将 DefaultValue 默认为 null。您也可以将该方法放在基础 class 中,以使其更整洁。
如果你想更改默认值,你只需要记住你正在调用它。
如果您想取消可能在属性上定义的其他一些内容,您也可以调用 this.ElementInformation.Properties[propertyName]
- 只是不要使用它来填充 DefaultValue
ConfigurationElement
类型有一个 ElementInformation
属性,后者又有一个 IsPresent
属性。
因此,与其尝试 return 空值 ConfigurationElement
,不如检查 IsPresent
属性 以查看 "whether the associated ConfigurationElement object is in the configuration file." 1
例如:
if (Configuration.Example.ElementInformation.IsPresent)
{
...
}
我选择使用更具可读性和可重用性的扩展方法ToNullIfEmpty()
。我保留了 DefaultValue 以防 non-intuitive 行为发生变化,将空字符串转换为 String.Empty.
[ConfigurationProperty("dataCenterRegion", DefaultValue = null)]
public string DataCenterRegion
{
get { return ((string)this["dataCenterRegion"]).ToNullIfEmpty(); }
set { this["dataCenterRegion"] = value; }
}
public static partial class ExtensionMethods
{
/// <summary>
/// Return null if the string is empty or is already null.
/// Otherwise, return the original string.
/// </summary>
public static string ToNullIfEmpty(this string str)
{
return String.IsNullOrEmpty(str) ? null : str;
}
/// <summary>
/// Return null if the string is white space, empty or is already null.
/// Otherwise, return the original string.
/// </summary>
public static string ToNullIfWhiteSpaceOrEmpty(this string str)
{
return String.IsNullOrWhiteSpace(str) ? null : str;
}
}
无需检查 null
的 属性 值,您可以轻松检查 属性 是否已在配置文件中设置或是否已返回默认值。这可以通过查看 ConfigurationElement
的 ElementInformation
.
ValueOrigin
来完成
// if not the default value...
if (MyConfigurationElement.ElementInformation.Properties["example"].ValueOrigin!=
PropertyValueOrigin.Default)
{
...
}
另请参阅 PropertyValueOrigin Enumeration 值的文档。