当一个对象引用只是一个私人成员“某种程度上被混淆”时,应该如何防御?

时间:2016-08-31 21:03:16

标签: c# casting ienumerable defensive-programming

考虑一下这个C#程序:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SandboxApplication
{
    public class IntsOwner
    {
        private List<int> _ints;

        public IntsOwner (IEnumerable<int> ints)
        {
            _ints = ints.OrderBy(i => i).ToList(); // They must be in the correct order
        }

        public IEnumerable<int> Ints
            => _ints;

        public void CheckFirstTwoInts ()
        {
            if (_ints.Count < 2)
            {
                Console.WriteLine("You need to collect some more ints before trying this.");
            }
            else if (_ints[0] <= _ints[1])
            {
                Console.WriteLine("Your ints are in the correct order and you should stop worrying.");
            }
            else
            {
                Console.WriteLine("You've failed, your highness.");
            }
        }
    }

    class Program
    {
        static void Main (string[] args)
        {
            var intsOwner = new IntsOwner(new List<int> {1, 2, 3, 4, 5});

            var ienumerable = intsOwner.Ints;

            var list = (List<int>)ienumerable;

            intsOwner.CheckFirstTwoInts();

            list[0] = 6;

            intsOwner.CheckFirstTwoInts();

            Console.ReadLine();
        }
    }
}

如果你运行它,你会得到两行输出:

Your ints are in the correct order and you should stop worrying.
You've failed, your highness.

IntsOwner类的原始设计者希望确保为私有成员_ints保留特定属性(列表元素的排序)。但是因为通过Ints属性返回对实际对象的引用,所以该类的用户可以修改该对象,以便该属性不再成立。

这种代码在实践中似乎不太可能,但仍然令人不安的是,对私有成员的控制可能会以这种方式“泄漏”。如果有的话,程序员应该试图阻止这种事情多少呢?例如,将Ints属性的表达式主体更改为_ints.Select(i = i)从而关闭这种修改私有成员的方式是合理的还是相称的?或者这对于代码的可读性会不会有不必要的偏执?

2 个答案:

答案 0 :(得分:2)

我总是在ToList

之后添加对AsReadOnly()的调用

不变性是关键,而不仅仅是返回类型中知识最少的原则

https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx

答案 1 :(得分:-3)

您可以执行类似

的操作
public IEnumerable<int> Ints => _ints.ToList<int>();

因此您不会返回对_ints的引用,而只返回复制列表。任何修改返回值的人都只会修改自己的副本,而不是私有存储的副本。