如何在类外部的私有只读数组中更改值?

时间:2017-12-14 19:32:08

标签: c# .net

请参阅以下代码:

long l = (gf.getFileLength() & 0xFFFFFFFFL);

请注意public class Program { private static void Main() { var c = new MonthClass(); string[] months = (string[])c.GetMonths(); months[0] = "Thursday"; var d = c.GetMonths(); Console.WriteLine(d.First()); } } public class MonthClass { private readonly string[] _months = { "January", "February", "March", "April","May","June","July", "August","September", "October", "November","December" }; public IEnumerable<string> GetMonths() => _months; } 是私有且只读。但是,对MonthClass._months的第二次调用会返回GetMonths作为数组的第一个元素,而不是Thursday

我如何更改January之外的私人只读成员的价值,我该如何防止这种情况?

阅读答案并做自己的研究后。我相信以下内容将解决这个问题:

MonthClass

3 个答案:

答案 0 :(得分:6)

GetMonths()返回对数组的引用。任何具有返回值的人都有对数组的引用,并且可以更改它。

那么如果你碰巧还保留了对从该公共方法返回的数组的私有引用呢? public方法返回对该对象的引用。 readonly适用于引用,而不适用于对象本身。

此代码功能相同:

public class C {
    private readonly string[] _array = new [] { "foo" };
    public string[] GetArray() => _array;
}

public class D {
    public static void Test() {
        C c = new C();
        c.GetArray()[0] = "bar";
    }
}

以下是修复方法:

public ReadOnlyCollection<String> GetMonths() {
    return Array.AsReadOnly(_months);
}

这将返回一个不同的实际对象,一个实际上禁止写作的对象 -  不只是对同一个数组的不同类型的引用。

您也可以返回ReadOnlyCollection而不是IEnumerable,因为您返回的实际对象是可索引的。

如果您想要返回一个可变的对象,但不允许任何人更改私有_months字段,则可以:

public List<string> GetMonths() => _months.ToList();

这样做:

var mutableMonths = c.GetMonths().ToList();

我恳请您不要将属性命名为“Get”,而不是从属性返回新对象。这是方法的合适案例。

答案 1 :(得分:1)

@EdPlunkett的回答是正确的。不过我想指出并添加一些东西。

该行

string[] months = (string[])c.GetMonths();

有些问题,因为GetMonths返回IEnumerable<string>,如果string[]的实现更改并返回除数组之外的其他内容,则转换为GetMonths将失败。您可以将行更改为

string[] months = c.GetMonths().ToArray();

请注意, 仍可以更改months中的元素,但这不会更改班级中的值。

如果你想坚持使用IEnumerable<string>,可以使用替代实施

public IEnumerable<string> GetMonths()
{
    foreach (var month in _months)
        yield return month;
}

另一种选择可能是使用索引器而不是GetMonths方法:

public string this[int index]
{
    get
    {
        return _months[index];
    }
}

答案 2 :(得分:0)

public class MonthClass
{
    private readonly ReadOnlyCollection<string> _months = new List<string>(12)
    {
        "January", "February", "March", "April","May","June","July",
        "August","September", "October", "November","December"
    }.AsReadOnly();

    public ReadOnlyCollection<string> GetMonths() => _months;
}

或只是

public class MonthClass
{
    public ReadOnlyCollection<string> Months { get; } = new List<string>(12)
    {
        "January", "February", "March", "April","May","June","July",
        "August","September", "October", "November","December"
    }.AsReadOnly();
}