表达式功能与自动属性相结合会导致问题

时间:2016-04-18 12:05:59

标签: c# .net c#-6.0 .net-4.6

在最终升级到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");

这怎么可能?表达式是否以不同方式解析在不同的时间?我在滥用语法吗?

3 个答案:

答案 0 :(得分:4)

让我们逐一了解你给出的选项,看看每个选项:

  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-property,这是一个隐藏(不可言说的)后备字段的属性。它汇编为:

    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),但在此之后,这两个字段不会相互影响。

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

答案 1 :(得分:2)

如果您希望行为完全相同,则可以使用以下两个选项:

  1. 有了体验(就像你提供的那样,并且不需要其他重构):

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

    public static string BootstrapBundle { get; private set; }
    
  3. 你的上一个例子不起作用的原因是,当你尝试分配字段变量时,字段变量没有值,在使用表达式主体时,每次访问属性时都会解析getter,并且赋值可以被推迟。换句话说,它只是作为readonly工作,并且变量的赋值必须在构造函数内部发生,使得字段变量无用,除非你想将它用于其他方法(这将是完全不可读的和糟糕的调试经验!) :)

    如果您希望上一个示例有效,则必须使用常量:

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

    但是如果您不需要默认值,那么您也不需要进行太多更改,您也可以将其删除。

答案 2 :(得分:0)

有几种方法可以定义属性:

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