C#中的朋友类用例的解决方法

时间:2011-05-07 14:13:54

标签: c# .net

考虑以下代码模式:

// Each foo keeps a reference to its manager
class Foo
{
    private FooManager m_manager;
}

// Manager keeps a list of all foos
class FooManager
{
    private List<Foo> m_foos;
}

问题:没有办法创建一个新的Foo并更新FooManager中的m_foos列表和新Foo实例中的m_manager引用,而不公开公开一些私有(并冒着某人用实际Foos取消列表的风险) 。

E.g。可以在Foo中实现构造函数Foo(FooManager manager)。它可以设置m_manager引用,但无法访问m_foos列表。或者您可以在管理器中实现CreateFoo()方法。它可以访问m_foos列表,但无法在Foo中设置m_manager。

在C ++中,人们显然会声明FooManager是Foo的朋友来表达设计意图,但这在C#中是不可能的。我也知道我可以让Foo成为FooManager的内部类以获得访问权限,但这也不是一个解决方案(如果Foo可以属于多个经理类,会怎样?)

顺便说一下。我知道.NET中的“内部”访问,但它要求Foo和FooManager在一个单独的程序集中独立存在,这是不可接受的。

没有公开私有内容的任何变通办法吗?

7 个答案:

答案 0 :(得分:7)

如果我理解正确的话:

public abstract class FooBus
{
    protected static FooBus m_bus;
}

public sealed class Foo : FooBus
{
    private FooManager m_manager;

    public Foo(FooManager fm)
    {
        if (fm == null)
        {
            throw new ArgumentNullException("Use FooManager.CreateFoo()");
        }

        if (m_bus != fm)
        {
            throw new ArgumentException("Use FooManager.CreateFoo()");
        }

        m_manager = fm;
    }
}

public class FooManager : FooBus
{
    private List<Foo> m_foos = new List<Foo>();

    public Foo CreateFoo()
    {
        m_bus = this;
        Foo f = new Foo(this);
        m_foos.Add(f);
        m_bus = null;

        return f;
    }
}

答案 1 :(得分:3)

一种选择是为Foo使用私有嵌套类来实现公共接口:

public interface IFoo
{
    // Foo's interface
}

public sealed class FooManager
{
    private readonly List<Foo> _foos = new List<Foo>();

    public IFoo CreateFoo()
    {
        var foo = new Foo(this);
        _foos.Add(foo);
        return foo;
    }

    private class Foo : IFoo
    {
        private readonly FooManager _manager;

        public Foo(FooManager manager)
        {
            _manager = manager;
        }
    }
}

由于Foo类是私有嵌套类,因此无法在FooManager外部创建,因此FooManager的CreateFoo()方法可确保所有内容保持同步。

答案 2 :(得分:3)

你可以做的是在不同类型的命名空间内创建你的类,让我们称之为“模块”(不要被class关键字欺骗,这不是real class):

public static partial class FooModule {

  // not visible outside this "module"
  private interface IFooSink {
    void Add(Foo foo);
  }

  public class Foo {
    private FooManager m_manager;
    public Foo(FooManager manager) {
      ((IFooSink)manager).Add(this);
      m_manager = manager;
    }
  }

  public class FooManager : IFooSink {
    private List<Foo> m_foos = new List<Foo>();
    void IFooSink.Add(Foo foo) {
      m_foos.Add(foo);
    }
  }

}

由于“module”是一个局部类,你仍然可以在同一个编译单元的其他文件中创建其中的其他成员。

答案 3 :(得分:0)

如何拥有基类:

class FooBase
{
     protected static readonly Dictionary<Foo,FooManager> _managerMapping = new Dictionary<Foo,FooManager>();
}

然后Foo和FooManager将FooBase作为基类,可以更新它们的映射,而不会在外部暴露它。然后你可以确定没有人会从外面改变这个集合。

然后在Foo中你有一个属性Manager,它将返回与之关联的管理器,类似于Manager中的Foos属性,它将提供所有Foos。

答案 4 :(得分:0)

在这个例子中,唯一可以使关系不同步的是Foo和Manager本身。在管理器上调用CreateFoo()来创建“托管foo”。其他人可以创建一个Foo,但是如果没有经理“同意”,他们就无法让它由管理者管理。

public class Foo
{
    private FooManager m_manager;

    public void SetManager(FooManager manager)
    {
        if (manager.ManagesFoo(this))
        {
            m_manager = manager;
        }
        else
        {
            throw new ArgumentException("Use Manager.CreateFoo() to create a managed Foo");
        }
    }
}

public class FooManager
{
    private List<Foo> m_foos = new List<Foo>();

    public Foo CreateFoo()
    {
        Foo foo = new Foo();
        m_foos.Add(foo);
        foo.SetManager(this);

        return foo;
    }

    public bool ManagesFoo(Foo foo)
    {
        return m_foos.Contains(foo);
    }
}

答案 5 :(得分:0)

我正在考虑使用反射。

设计失败没有好的解决方案(CLR中没有朋友)。

答案 6 :(得分:0)

这样的事情怎么样:

public class FriendClass
{
     public void DoSomethingInMain()
     {
         MainClass.FriendOnly(this);
     }
}

public class MainClass
{
    public static void FriendOnly(object Caller)
    {
        if (!(Caller is FriendClass) /* Throw exception or return */;

        // Code
    }
}

当然,这并不妨碍用户做这样的事情:

public class NonFriendClass
{
    public void DoSomething()
    {
         MainClass.FriendOnly(new FriendClass());
    }
}

我想可以有办法解决这个问题,但我的任何想法都开始变得过度。