具有Null DefaultValue

时间:2015-04-22 13:38:03

标签: c# .net string configuration app-config

我将以下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"]返回String.Empty,其中baseConfigurationElement(索引器在此定义:https://msdn.microsoft.com/en-us/library/c8693ks1(v=vs.110).aspx),但我仍然不是确定为什么它没有采用null的值。

更新

即使DefaultValue = default(string)将字符串设置为String.Empty

更新

如果配置中不存在该属性,则base.Properties.Contains("example")会返回true

5 个答案:

答案 0 :(得分:9)

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。< / p>

第一个if块提示可能解释这种奇特的行为。 [ConfigurationProperty]属性的DefaultValue属性是可选的。如果程序员未设置DefaultValue,则默认为null。第一个if块使用默认null来检查是否指定了DefaultValue。如果没有,它会回退到从[DefaultValue]属性中获取默认值(如果存在这样的属性)。

这一切都意味着:指定DefaultValue = null具有与根本不指定它相同的效果,在这种情况下,配置子系统为字符串选择“理智”默认值:空字符串。

解决方法:

这是一个有点hacky的解决方法:不要将配置属性声明为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;
    }
}

以下是IncognitoStringIncognitoStringConverter的实施方式:

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,因此您可以将属性值分配给任何字符串变量。我知道,它只是为了获得可以为空的属性而且非常复杂。也许只是忍受空字符串。

答案 1 :(得分:3)

另一种解决方法是拨打这样的电话:

[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。您也可以将该方法放在基类中,使其更加整洁。

如果你想改变默认值,你只需要记住你正在调用它。

如果你想提升你可能在属性上定义的其他一些内容,你也可以拨打this.ElementInformation.Properties[propertyName]的电话 - 只是不要用它来填充DefaultValue

答案 2 :(得分:2)

您可以轻松检查配置文件中是否已设置属性,或者是否已返回默认值,而不是检查null的属性值。这可以通过查看ValueOrigin的{​​{1}}中的ConfigurationElement来完成。

ElementInformation

另请参阅PropertyValueOrigin Enumeration的值的文档。

答案 3 :(得分:0)

ConfigurationElement类型具有ElementInformation属性,而该属性又具有IsPresent属性。

因此,不是尝试返回null ConfigurationElement,而是检查IsPresent属性以查看“关联的ConfigurationElement对象是否在配置文件中”。 1

例如:

if (Configuration.Example.ElementInformation.IsPresent)
{
    ...
}

答案 4 :(得分:0)

我选择使用更具可读性和可重用性的扩展方法ToNullIfEmpty()。我将DefaultValue保留在原位,以防对将空字符串转换为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;
    }
}