【问题标题】:simulating C++ friendship in C# with partial classes在 C# 中使用部分类模拟 C++ 友谊
【发布时间】:2014-07-17 21:04:32
【问题描述】:

我有以下情况:

我需要创建一个树状结构,一旦实例化,它就是不可变的。例如,考虑一个列表方法的列表。问题是在构建过程中,我需要能够插入节点。所以我需要某种允许插入节点的私有接口,而这个结构的公共接口只允许检索它们。我试过这个:

public partial class TreeLike {
  public SomeType1 SomeTreeProperty1 { get; private set; }
  public SomeType2 SomeTreeProperty2 { get; private set; }

  private List<NodeLike> _Nodes = new List<NodeLike>();
  public ReadOnlyCollection<NodeLike> Nodes {
    get {
      return _Nodes.AsReadOnly();
    }
  }
}

public class NodeLike {
  public SomeType1 SomeNodeProperty { get; private set; }

  private List<NodeLike> _Children = new List<NodeLike>();
  public ReadOnlyCollection<NodeLike> Children {
    get {
      return _Children.AsReadOnly();
    }
  }

  public partial class TreeLike {
    public SomeType3 SomeTreeProperty3 { get; private set; }

    TreeLike() {
      NodeLike n0 = new NodeLike();
      NodeLike n1 = new NodeLike();
      n0._Children.Add(n1);
      _Nodes.Add(n0);
    }
  }
}

这个问题(除了在NodeLike 中继续TreeLike 的声明/定义有点hackysh 外观)是,虽然它有效,但TreeLikeSomeTreeProperty3 对外部世界不可见。也就是说,如果我在最外层范围创建TreeLike 的实例,我只能访问第一个在部分类的“全局”范围声明中声明的属性。

所以我想知道是否有办法让在局部类的嵌套延续中声明的属性和方法仍然对全局范围可见(因此,这个类的客户端)。或者,如果不是,那么更好的 C# 惯用方式是什么? 也许创建类的不可变版本?

【问题讨论】:

  • protected(相对于私人)不做你想做的事吗?
  • 什么意思?使列表受到保护?这些类没有继承关系。我误会你了吗?
  • 为什么不给 NodeLike 一个构造函数,它接受另一个 NodeLike 并将其添加为子对象?
  • TreeLikeNodeLike.TreeLike 是两个不同的类。在这种情况下,partial 修饰符没有任何作用。
  • 对不起,我把 protected 和 internal 混淆了。有什么理由不工作吗?

标签: c# partial-classes


【解决方案1】:

您应该简单地在构造函数中执行所有初始化,因为它允许您使用只读字段实现真正的不变性。

例如,如果你的节点接口是这样定义的:

interface INode
{
    public string Name { get; }
    public ReadOnlyCollection<INode> Children { get; }
}

那么实现应该是:

class Node : INode
{
    private readonly string _name;
    public string Name
    {
        get { return _name; }
    }

    private readonly ReadOnlyCollection<INode> _children;
    public ReadOnlyCollection<INode> Children
    {
        get { return _children; }
    }

    public Node(string name, IEnumerable<INode> children)
    {
        _name = name;
        _children = new List<INode>(children).AsReadOnly();
    }

    public Node(string name, params INode[] children)
        : this(name, (IEnumerable<INode>)children)
    { }
}

最后一个构造函数重载使用params 关键字允许您直接通过构造函数传递子节点,这意味着您可以这样做:

var root = new Node(
    "root",
    new Node("left",
        new Node("left-left"),
        new Node("left-right")),
    new Node("right",
        new Node("right-left"),
        new Node("right-right"))
    );

【讨论】:

    【解决方案2】:

    满足您的设计要求的可能解决方案如下所示:

    public partial class TreeLike {
        public SomeType1 SomeTreeProperty1 { get; private set; }
        public SomeType2 SomeTreeProperty2 { get; private set; }
        public SomeType3 SomeTreeProperty3 { get; private set; }
    
        private List<NodeLike> _Nodes = new List<NodeLike>();
        public ReadOnlyCollection<NodeLike> Nodes {
            get {
                return _Nodes.AsReadOnly();
            }
        }
    
        TreeLike() {
            NodeLike n1 = new NodeLike();
            NodeLike n0 = new NodeLike(n1);
    
            _Nodes.Add(n0);
        }
    }
    
    public class NodeLike {
        public SomeType1 SomeNodeProperty { get; private set; }
    
        private List<NodeLike> _Children = new List<NodeLike>();
        public ReadOnlyCollection<NodeLike> Children {
            get {
                return _Children.AsReadOnly();
        }
    
        public NodeLike(params NodeLike[] children) {
            _Children = children.ToList();
        }
    }
    

    partial 与此问题无关。 params 是一种有用的语法,它还可以让您执行以下操作:

    new NodeLike(childNode1, childNode2, childNode3);
    

    旁注:埃及大括号是discouraged in C#

    【讨论】:

      猜你喜欢
      • 2015-12-11
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-29
      • 2014-01-13
      • 2010-12-10
      • 2014-09-30
      相关资源
      最近更新 更多