Expression-Bodied 函数与自动属性结合导致问题

Expression-Bodied Function combined with Auto Properties results in problems

在最终升级到 VS2015 并开始使用 .NET4.6 之后,我一直在使用一些旧的语法糖 类。

不幸的是,这并不总是一帆风顺:/ 下面是一个例子。

我有这个可用的现有代码。

private static string _bootstrapBundle;
public static string BootstrapBundle
{
    get
    {
        return _bootstrapBundle;
    }
}

使用表达式主体的快速重写给了我这个,它有效

private static string _bootstrapBundle;
public static string BootstrapBundle => _bootstrapBundle;

这也可以重写为使用自动属性,就像下面的代码一样有效

public static string BootstrapBundle { get; private set; }

如果我尝试更进一步,写下下面的内容,那是行不通的

private static string _bootstrapBundle;
public static string BootstrapBundle { get; private set; } = _bootstrapBundle;

所有三个代码示例都可以正常编译,但是当我稍后尝试像下面这样分配一个值时,只有最后一段代码无法为 BootstrapBundle 分配任何内容。

BootstrapBundle = SquishIt.Framework.Bundle.Css()
                    .Add("/assets/stylesheets/Theme/" + theme + "/Bootstrap/bootstrap.less")
                    .Render("/assets/Cache/bootstrap.css");

怎么会这样?表达式的解析方式不同吗?在不同的时间?我在滥用语法吗?

有几种定义属性的方法:

// this defines a public getter, public setter property with no backing field (there is an internal one, but not one you can access)
public static string BootstrapBundle {
    get;
    set;
} 

// this defines a public getter, public setter property with a backing field
private static string _bootstrapBundle = "42";
public static string BootstrapBundle {
    get {
        return _bootstrapBundle;
    }
    set {
        _bootstrapBundle = value;
    }
}

//this defines a no setter property with a backing field
private static string _bootstrapBundle = "42";
public static string BootstrapBundle {
    get {
        return _bootstrapBundle;
    }
}

使用 C#6 功能:

// this sets a getter only property that returns the current value of _bootstrapBundle (equivalent to the last form in the code above)
private static string _bootstrapBundle = "42";
public static string BootstrapBundle => _bootstrapBundle;

// this sets up an auto property (no backing field) that at initialization gets the initial value of _bootstrapBundle
private static string _bootstrapBundle = "42";
public static string BootstrapBundle {get;set;} = _bootstrapBundle;

// equivalent to this:
public static string BootstrapBundle {get;set;} = "42";

由于您在代码中设置了 属性,这意味着您需要一个 setter。如果您想要的只是一个带有支持字段的 属性,那么您没有 C#6 语法来替换旧的 return _backingField;/_backingField=value

如果您希望行为完全相同,您有以下两种选择:

  1. 具有表达式主体(就像您提供的那样,并且不需要其他重构):

    private static string _bootstrapBundle;
    public static string BootstrapBundle => _bootstrapBundle;
    
  2. 使用自动属性(就像你也建议的那样,在这里你必须重构所有赋值以使用 属性 而不是字段变量):

    public static string BootstrapBundle { get; private set; }
    

上一个示例不起作用的原因是,当您尝试分配字段变量时它没有值,因为使用表达式正文,每次访问时都会解析 getter 属性,赋值可以延迟。换句话说,它以只读方式工作,并且变量的赋值必须在构造函数内部发生,从而使字段变量无用,除非您想将它用于其他方法(这将是完全不可读的并且调试体验很糟糕!) :)

如果你想让你的最后一个例子起作用,你必须改用常量:

public static string BootstrapBundle { get; private set; } = "42";

但如果您不需要默认值,则无需进行太多更改,您也可以将其保留。

让我们逐一浏览您给出的选项,看看每个选项的作用:

  1. private static string _bootstrapBundle;
    public static string BootstrapBundle
    {
        get
        {
            return _bootstrapBundle;
        }
    }
    

    我假设我不必解释它的作用。但请注意,如果您尝试分配给 BootstrapBundle,它将在编译时失败,因为没有 setter。但是您可以通过直接分配给字段来解决这个问题。

  2. private static string _bootstrapBundle;
    public static string BootstrapBundle => _bootstrapBundle;
    

    这与#1 完全相同,只是语法更简洁。

  3. public static string BootstrapBundle { get; private set; }
    

    这里我们有一个 auto-属性,这是一个 属性 带有隐藏的(不可描述的)支持字段。它编译为:

    private static string <BootstrapBundle>k__BackingField;
    public static string BootstrapBundle
    {
        get
        {
            return <BootstrapBundle>k__BackingField;
        }
        private set
        {
            <BootstrapBundle>k__BackingField = value;
        }
    }
    

    这意味着设置 属性 现在有效,设置后获取它将为您提供新值。

  4. private static string _bootstrapBundle;
    public static string BootstrapBundle { get; private set; } = _bootstrapBundle;
    

    这与#3 相同,只是隐藏的支持字段被初始化为您提供的值:

    private static string _bootstrapBundle;
    private static string <BootstrapBundle>k__BackingField = _bootstrapBundle;
    public static string BootstrapBundle
    {
        get
        {
            return <BootstrapBundle>k__BackingField;
        }
        private set
        {
            <BootstrapBundle>k__BackingField = value;
        }
    }
    

    这意味着现在有两个字段:一个隐藏的和一个可见的。隐藏字段最初会设置为可见字段的值(即null),但之后,两个字段将不会相互影响。

    这意味着如果您设置属性,然后获取属性,您将获得更新后的值。但是如果你读取可见字段,它的值将不会被更新。反之亦然:如果您更新字段,属性 的值不会改变。