使用函数调用时,C#6.0的默认属性值如何分配并受范围影响?

时间:2017-04-19 12:12:55

标签: c# garbage-collection c#-6.0

我可能会在这里得到一些downvotes,但我实际上通过正常搜索找到了冲突的信息,并希望得到其他人也能轻易找到的确定答案。

给出当前C#中的属性:

public static IEnumerable<string> foo { get; set; } = new string[] { "bar", "bar2" };

我们知道foo的默认值将返回上面的数组。如果指定了其他值,则不再使用默认值。但是,如果案件是:

public static IEnumerable<string> foo { get; set; } = GetMyStrings();

我的想法是这个功能如下:

if(foo == null) { foo = GetMyStrings();}

并且foo将保留对象生命周期中第一次运行GetMyStrings()的值,除非被手动指定的值覆盖。

我的同事坚持认为这可能会导致GC问题,并且GetMyStrings()的结果可能超出范围并被收集,“重新整理”参数并在整个生命周期内导致多次调用GetMyStrings()物体。

我们哪个是正确的?

3 个答案:

答案 0 :(得分:5)

不,实际上是这样的:

static ClassName()
{
    foo = GetMyStrings();
}

编译器生成一个静态构造函数并将赋值调用放在那里(就像它在构造函数中为非静态属性创建一行)。

  

我的同事坚持认为这可能会导致GC问题,并且GetMyStrings()的结果可能超出范围并被收集,“重新整理”参数并在对象的生命周期内导致多次调用GetMyStrings()。 / p>

他正在胡说八道。无论从何处分配实例,分配的实例都不会被垃圾回收。

答案 1 :(得分:0)

写这个

public static IEnumerable<string> Foo { get; set; } = GetMyStrings();

是这个宣言的语法糖

public static IEnumerable<string> Foo { get; set; }

并添加了一个如下所示的静态构造函数:

static MyClass() {
    Foo = GetMyStrings();
}

(如果你已经有一个静态构造函数,则会隐式添加Foo = GetMyStrings();行。)

除此之外,没有区别:once it is time for the class to get initializedGetMyStrings()从构造函数中调用,然后它返回的值被分配给Foo。之后,该值保持不变,直到被另一个替换,或者程序结束。

&#34; renullifying&#34;参数,它永远不会发生在类对象的同一个实例上。当包含该类的应用程序域被卸载时,或者当该类加载到另一个应用程序域时,您可以多次调用GetMyStrings(),但这与新的C#6语法无关。

最后,如果您不打算在静态构造函数之后更改Foo,请考虑将其设置为只读:

public static IEnumerable<string> Foo { get; } = GetMyStrings();

答案 2 :(得分:0)

举例说明我的评论,请看这个例子:

public void RunTest()
{
    Test t = new Stackoverflow.Form1.Test();
    Console.WriteLine(t.Values.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values.First());

    Console.WriteLine("------");
    Console.WriteLine(t.Values2.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values2.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values2.First());

    Console.WriteLine("------");
    Console.WriteLine(t.Values3.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values3.First());
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(t.Values3.First());
}

public class Test
{
    public IEnumerable<string> Values { get; set; } = GetValues();

    public static IEnumerable<string> GetValues()
    {
        List<string> results = new List<string>();
        for (int i = 0; i < 10; ++i)
        {
            yield return DateTime.UtcNow.AddMinutes(i).ToString();
        }
    }

    public IEnumerable<string> Values2 { get; set; } = GetValues2();

    public static IEnumerable<string> GetValues2()
    {
        return GetValues().ToList();
    }

    public IEnumerable<string> Values3 { get; set; } = GetValues().ToList();
}

这个输出是:

19/04/2017 12:24:25
19/04/2017 12:24:26
19/04/2017 12:24:27
------
19/04/2017 12:24:25
19/04/2017 12:24:25
19/04/2017 12:24:25
------
19/04/2017 12:24:25
19/04/2017 12:24:25
19/04/2017 12:24:25

值:如您所见,IEnumerable&lt;&gt;每次调用时,都会评估使用yield的方法。

Values2:该方法返回List。由于List实现了IEmnumerable,你仍然只是返回List的具体实例(虽然表现为它的IEnumerable接口),因此它只被评估一次。

Values3:我们基本上将.ToList()移到GetValues2()方法之外,并直接在属性上实现它。这里的结果与Values2相同,原因基本相同。

当你返回一个实现IEnumerable的具体对象时,它将被评估一次,但要注意第一个例子。

记住伙计们,Linq是一个观点。如果基础数据发生变化,那么从Linq表达式返回的内容也会发生变化。

重新设置垃圾回收问题:活动对象引用的对象不会被垃圾回收。