共同朋友课程的C#解决方法

时间:2019-03-27 20:14:03

标签: c# inner-classes encapsulation friend

因此,我有一个类似于this one的用例,但我认为还有一些其他细节需要提出一个新问题。 (related questions,以供参考)

我正在写一个实现a cycle的数据结构。基本设计是这样的:

public class Cycle<T>
{
    public Node<T> Origin { get; private set; }
    public int Count { get; private set; }
}

public class Node<T>
{
    public Cycle<T> Cycle { get; private set; }
    public Node<T> Next { get; private set; }
    public Node<T> Previous { get; private set; }
    public T Value { get; set; }
}

但是,我要实现以下行为的 all

  1. 更新Cycle.Count,插入/删除节点
  2. 允许“空” Cycle(即Origin = null)为Node创建新的Origin
  3. 在案例1和案例2之外,防止构造新的Node对象
  4. 避免暴露不需要被这两个类以外的类调用的方法

在C ++中,我只是将每个类都设为另一个friend。但是在C#中,我没有找到一种使之起作用的方法。

我知道,如果我将Node嵌套在Cycle内并且仅公开Node的单个构造函数,那么我将可以完成除#3之外的所有任务,就像这样:

public class Cycle<T>
{
    public Node Origin { get; private set; }
    public int Count { get; private set; }

    public class Node
    {
        public Cycle<T> Cycle { get; private set; }
        public Node Next { get; private set; }
        public Node Previous { get; private set; }
        public T Value { get; set; }

        internal Node<T>(Cycle<T> cycle)
        {
            if (cycle.Origin != null)
                throw new InvalidOperationException();
            else
            {
                Cycle = cycle;
                Next = this;
                Previous = this;
                cycle.Origin = this;
                cycle.Count = 1;
            }
        }
    }
}

但是正如您所看到的,我只能走到internal,所以我仍然必须验证数据完整性或破坏封装。

我确实有一个“聪明”的想法,但这有点像黑魔法答案:

public abstract class Cycle<T>
{
    public Node Origin { get; private set; }
    public int Count { get; private set; }

    public sealed class Node
    {
        private CycleInternal _cycle;
        public Cycle<T> Cycle { get { return _cycle; } }
        public Node Next { get; private set; }
        public Node Previous { get; private set; }
        public T Value { get; set; }

        // this constructor can be called by CycleInternal, but not other classes!
        private Node(CycleInternal cycle)
        {
            Cycle = cycle;
            Next = this;
            Previous = this;
            cycle.Origin = this;
            cycle.Count = 1;
        }

        private sealed class CycleInternal :  Cycle<T>
        {
            // this constructor can be called by Node, but not other classes!
            public CycleInternal() {}
        }
    }
}

在这种情况下,我担心其他事情可能会从Cycle中继承下来;通过将构造函数设为Cycle私有可以防止这种情况发生?还是我只是在这里偏执?

1 个答案:

答案 0 :(得分:6)

  

internal将其公开给同一程序集中的其他类,我不想这么做

我的建议是:克服这种恐惧并将其标记为internal

我听说过此功能请求-C#实现C ++样式的朋友语义-很多很多次。该功能通常是由于担心“如果我允许我的议会中的任何班级参加内部状态聚会,我的同事就会滥用特权”。

但是您的同事已经可以滥用该特权,因为您的同事已经可以添加朋友(在C ++中)或建立后门内部方法(在C#)。

C#可访问性系统的设计根本不是为了保护您免受那些对源代码具有写访问权的人对您的利益怀有敌意的情况。正如private的意思是“这是此类的实现细节”,而protected的意思是“这是该层次结构的实现细节,internal的意思是“这是该程序集的实现细节。 “。如果不能信任负责该程序集的正确操作的同事明智地使用自己的权力,那就请更好的同事。如果您的类型需要访问彼此的内部细节以使整个程序集正常工作,这就是internal的用途,所以请使用它。

或者换句话说,这是一个社会问题,而不是技术问题,因此您应该通过施加社会压力来解决。 C#可访问性系统的设计目的不是解决应通过代码审查解决的问题。如果您的同事在滥用权限,那么代码审查时间就是使他们停止的时间,就像您使用代码审查阻止他们将任何其他错误代码添加到项目中一样。

要回答您的特定问题:

  

我担心的是其他东西可能会从Cycle中继承下来;通过将构造函数设为Cycle私有可以防止这种情况发生?

是的,抽象类可以具有私有构造函数,然后唯一的派生类型是嵌套类型。

我经常使用该模式,并且将嵌套类型设为私有。在C#中,将嵌套类型设为公共是不好的气味。

此外:通常,我这样做是为了创建“案例类”,例如:

abstract class ImmutableStack<T>
{
  private ImmutableStack() { }
  private sealed class EmptyStack : ImmutableStack<T> { ... }
  private sealed class NormalStack : ImmutableStack<T> { ... }

,依此类推。这是使类型的实现详细信息分布在多个类中,但仍将它们封装到单个类中的一种好方法。

  

我在这里只是偏执吗?

在我看来,是的。