共同朋友类的C#解决方法

问题描述 投票:4回答:1

所以,我有一个类似于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; }
}

但是,我想实现以下所有行为:

  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继承;可以通过使构造函数循环私有来防止?或者我只是在这里偏执?

c# inner-classes encapsulation friend
1个回答
6
投票

internal将它暴露给同一个程序集中的其他类,我不想这样做

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

我听过这个功能请求 - C#实现了C ++风格的朋友语义 - 很多次。该功能通常受到以下问题的激励:“如果我允许我的集会中的任何一个班级参与内部状态,我的同事就会滥用这一特权”。

但是你的同事已经有能力滥用这个特权,因为你的同事已经可以添加朋友(用C ++)或制作后门的内部方法(在C#中)。

C#辅助功能系统的设计目的并不是为了保护您免受那些对您的源代码具有写入权限的人对您的兴趣产生怀疑的情况。就像private的意思是“这是这个类的实现细节”而protected的意思是“这是这个层次结构的实现细节,internal的意思是”这是这个程序集的实现细节“。如果你的同事负责正确的操作不能信任该组件明智地使用他们的权力,获得更好的同事。如果你的类型需要访问彼此的内部细节,使整个组件正常工作,这就是internal的用途,所以使用它。

换句话说,这是一个社会问题,而不是技术问题,所以你应该通过施加社会压力来解决它。 C#辅助功能系统不是为解决代码审查应解决的问题而设计的。如果您的同事滥用他们的权限,代码审查时间是让他们停止的时间,就像您使用代码审查阻止他们向您的项目添加任何其他错误代码一样。

回答您的具体问题:

我担心的是其他东西可以从Cycle继承;可以通过使构造函数循环私有来防止?

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

我经常使用该模式,并且我将嵌套类型设为私有。在C#中使用公开的嵌套类型是一种难闻的气味。

旁白:通常我会这样做“案例类”,如:

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

等等。这是一种很好的方法,可以将类型的实现细节分布在多个类中,但仍然全部封装在一个类中。

我只是在这里偏执吗?

听起来像是的。

© www.soinside.com 2019 - 2024. All rights reserved.