0

我很喜欢 C++ 的友谊特性。说,我们有一个父母和孩子。Child 的 ctor 将 Parent 作为参数:

Child::Child(Parent & parent)
{
    // Parent::RegisterChild is private
    parent.RegisterChild(this);
}

由于友谊,Parent 和 Child 可能对自己保持相当安全的控制 - 例如,当 Child 更改 Parent 时,它可能会通知以前的 Parent,它想从它的 Child 列表中分离和新的 Parent,它想被附加到它的列表。此解决方案中的安全性意味着,派生自 Parent 或 Child 的任何类都不会破坏此机制。

不幸的是,C# 不提供友谊这样的特性。有一个internal访问修饰符,但它限制了类元素对程序集的可见性,因此如果扩展程序集并引入新类,则该机制将不再安全。并且为了提供安全性而将这些类提取到单独的程序集中似乎是一个非常混乱的想法。

有没有办法使用现有的 C# 机制在两个类之间提供如此紧密和安全的合作,而这在派生类中是无法打破的?


编辑:回应评论

这不是信任问题,因为 - 正如 Eric 所说 - 有权访问源代码的人总是可以破坏它(删除私有修饰符,添加另一个朋友类,等等)。这是一种安全措施,旨在防止人们犯简单、愚蠢的错误,这些错误后来很难追踪。我使用朋友创建隔离机制,嵌入在基类中,在派生类中不能(或至少不容易)被破坏。这样我和我的同事都不必再担心这些了。

4

2 回答 2

3

唯一基于类型的访问是嵌套类型。如果将一种类型嵌套在另一种类型中,则嵌套类型可以访问封闭类的所有成员,包括私有成员。嵌套类型也可以是私有的。例如:

public class Outer
{
    // Code outside the body of Outer cannot call this. But
    // the code in Nested *is* inside the body of Outer, so it's okay.
    private void Foo()
    {
    }

    private class Nested
    {
        internal void Bar()
        {
            new Outer().Foo();
        }
    }
}

这在某些情况下可能会有所帮助,但显然不能替代 C++ 中“朋友”类的概念

需要注意的一件事是InternalsVisibleToAttribute,它允许一个程序集访问另一个程序集的内部成员。我意识到在您的情况下,您实际上需要比更多的受限访问internal,但[InternalsVisibleTo]仍然值得了解 - 它对于测试特别有用。

于 2013-10-08T05:52:13.090 回答
0

这是我为我的项目实施的基本思想,效果很好。希望它也对你有用。请注意,这仅在运行时强制建立友谊(这听起来不太好)。

public interface IFriendKey { object Id {get; set;} }

class Friend<TFriend>
{
    protected void FriendAssert(IFriendKey key)
    {
        if ( key == null || key.Id == null || key.Id.GetType() != typeof(TFriend) )
            throw new Exception("No right to execute the called method.");
    }
}

class A : Friend<B>
{
    public void f(IFriendKey key)
    {
        FriendAssert(key);
        Console.WriteLine("ONLY class B can execute this method successfully, even though it is declared public.");
    }
}

class B
{
    private class AFriendKey : IFriendKey 
    {
        public object Id {get; set;}
    }

    IFriendKey Key { get { return new AFriendKey() {Id = this}; } }

    public void g()
    {
        new A().f(this.Key); 
    }
}

public class Test
{
    public static void Main()
    {
        new B().g();
    }
}

在线演示

希望有帮助。

于 2013-10-08T07:18:12.183 回答