C#将成员标记为“不使用”

25
public class Demo
{
    private List<string> _items;
    private List<string> Items
    {
        get
        {
            if (_items == null)
                _items = ExpensiveOperation();

            return _items;
        }
    }
}

Demo 类中的其他方法将可以访问 _items 字段。由于我使用了属性来延迟加载项,因此我不希望另一个开发人员错误地尝试使用 _items 字段。

我知道有 ObsoleteAttribute 可以使用,但该字段技术上并不过时。

是否有更好的方法将成员标记为 "不要使用"?


27
如果你不信任团队中的开发人员,那么你就有更大的问题需要担心。 - jason
19
这是私有的。责任就在此。如果有多个开发人员在类的私有部分上工作,那么他们应该进行充分的沟通。至少要吃顿饭看场电影。这不是你可以强制执行的事情,你只能通过良好的设计来避免它。 - Hans Passant
4
评论对解决这种问题的帮助很大! - spender
3
@Jason 你说得对,按照这种思路,我们应该将每个属性/字段/方法都设为公共的,然后相信其他开发人员。 - Rob
3
@Jason,恐怕你错了。根据你对“信任”的理解,我完全相信我的团队有能力且不会故意做出恶意行为;但我并不天真地认为他们都是完美无缺的,从不犯错误。因此,这是一个合法的问题,我认为C#中的访问修饰符粗糙而且不够充分,因为它允许合理的日常错误在可以通过最少的编译器支持自动检测时被忽略。 - Timwi
显示剩余8条评论
7个回答

48

尽管这不是您想要做的一般技术(也没有一个通用的方法,正如其他答案所涵盖的那样,您需要信任其他开发人员),但在这种情况下,您可以创建一个 Lazy<List<T>>(假设使用 .NET 4 或更高版本 - 尽管容易向后兼容)

class Demo {
    readonly Lazy<List<string>> _items;
    public Demo() {
        var _items = new Lazy<List<string>>( ExpensiveOperation);
    }
    List<string> Items { get { return _items.Value; }}
 }

readonly / 不可变的方法通常是处理后备字段的方式。

编辑:根据@Timwi的答案(如果你喜欢这个想法就点+1),可以更进一步,以JavaScript的方式使用基于能力的限制,甚至不暴露Lazy字段,而只是封闭在操作中(还包含了@Mr Disappointment的ReadOnlyCollection建议):

class Demo {
    readonly Func<ReadOnlyCollection<string>> _getItems;
    public Demo() {
        var items = new Lazy<List<string>>( ExpensiveOperation);
        _getItems = () => items.Value.AsReadOnly();
    }
    ReadOnlyCollection<string> Items { get { return _getItems(); }}
 }

这样我们的愚蠢代码技巧帖子就要结束了。


3
我认为这是正确的做法,因为它排除了可能被滥用的领域的使用。 - flq
6
是的,虽然其他开发人员仍然可以直接访问_items,但这并不重要。该字段无法被覆盖(它是只读的),并且初始化逻辑由Lazy<T>类保证运行。如果希望ExpensiveOperation仅在多个线程同时访问该字段时执行一次,请考虑在构造函数中提供LazyThreadSafetyMode.ExecutionAndPublication参数。 - porges
1
我对这种做事方式非常赞同。它避免了今后可能出现的任何混淆(而不是依赖于某个无用的未来开发人员实际阅读任何内容)。然而,我现在要质疑的是,是什么阻止了这个开发人员直接调用 ExpensiveOperation 呢? - spender
2
这难道不是让开发人员多看一眼就变得复杂了吗?它只限制了 _items 及其底层列表的重新赋值,这意味着元素仍然可以被操作。而这可以通过 readonly List<string> _items 并按需填充来实现。也许应该使用 readonly ReadOnlyCollection<string> - Grant Thomas
2
"[...] 而且并没有一个,正如其他答案所涵盖的那样,你需要信任其他开发人员" - 这不是真的,请看我的回答。 - Timwi
显示剩余5条评论

45

_items字段重命名为_heyFutureDeveloperDoNotReferenceThisFieldMmmkay


13
如果您设置了这样的属性,那么 Items getter 如何访问它而不生成与您要查找的内容相同的警告/错误?
“另一个开发人员”是什么意思?如果您指的是在同一代码中与您合作的另一位开发人员,那么简单的注释可以像这样:
///<summary>Do not access directly, using lazy initialization in getter.</summary>

当你将鼠标悬停在该字段上时,Visual Studio将显示此信息,应该足够使用。

如果你是指有人正在使用这个类,那么这就是信息隐藏的全部意义所在,你可以放心使用。


“如果不想生成相同的警告,Items getter 该如何访问它?”——嗯,可以使用 #pragma disable,但我同意这并不是一个好方法。 - Timwi

6

是的,有一种相对简单的方法。有些人可能认为它是黑客方法,但我认为它是合法的。它利用了本地变量作用域规则来实现您想要的方法级别的隐私:

public class Demo
{
    private readonly Func<List<string>> _getItems;

    public Demo()
    {
        List<string> items = null;
        _getItems = () =>
        {
            if (items == null)
                items = ExpensiveOperation();
            return items;
        };
    }

    public List<string> Items { get { return _getItems(); } }
}

现在变量items已正确作用于使用它的方法范围内。仍然可以访问_getItems,但这是不必要的,因为它与Items完全相同。不能修改items,因为它是局部变量,也不能修改_getItems,因为它是只读的。

1
在深思熟虑后,我认为这是一个好主意,并且展示了我可以应用于我的答案中的重构 - 请看那里。 - Ruben Bartelink

4

我认为这是一个非常有价值的想法。不幸的是,C#并没有考虑到这一点。因此,在实践中,在当前版本的语言中,你真正能做的事情并不多。

如果你不信任团队中的开发人员,那么你就有更大的问题要担心。

强烈反对;如果你信任任何人,甚至是你自己,那么你就有问题了。最好的代码不信任任何人。最好的代码是防弹的。它不留给任何人犯错的机会。

C#并不能完全实现这一点,尽管它比许多其他语言更接近。

它是私有的。这就是止步之地。如果超过一个开发人员在类的私有部分工作,那么他们应该进行非常良好的沟通。

基本上是相同的论点。是的,它是私有的,但又怎样呢?为什么把私有细节封装好不是一个有效的想法呢?

P.S. 我提出了一个相关的(但不完全相关)实用方法来更好地保护C#代码的防弹性,但我认为很少有人看到它。所以在这里 :) 我希望看到一个实用的方法来封装私有成员。


1

什么也不做。

对于一个良好编写的类的公共或受保护成员,我们期望访问它以一种明智的方式工作,如果不是这样的情况,应该有很好的文档记录。

对于私有字段,我们没有理由做出这样的假设。

此外,如果我作为团队中的新开发人员来到这个类,我不知道_items是做什么的。我能做什么?在我查看现有代码对其进行了什么操作之前,我无法执行任何有意义的工作。而且,如果有即使只有一点点的文档注释,我会看到它是懒加载属性的后备字段。

这不像你可以随意处理其他私有字段并期望它正常工作。


-2

怎么样?

private Dictionary<String,Object> PandorasCoochie = new Dictionary<String,Object>()

然后将您的不可触及的属性支持者放在里面。


它们变成动态声明的对象,不会显示在开发工具中。 - Mark Robbins
1
为什么我们不使用动态语言,这样就没有静态分析可用了! - Jonathon Reinhart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接