如何克服C#中缺乏友谊?

时间:2013-10-08 05:48:12

标签: c# c++ friend

我非常喜欢C ++的友谊功能。比如,我们有一个父母和孩子。 Child的ctor将Parent作为参数:

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

由于友谊,父母和孩子可以保持对自己的非常安全的控制 - 例如,当Child更改Parent时,它可以通知先前的Parent,它想要从其子列表和新的Parent中分离,它想要附在其清单上。此解决方案中的安全性意味着,不会从父或子派生的类可能会破坏此机制。

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

有没有办法使用现有的C#机制在两个类之间提供这种紧密而安全的合作,这在派生类中无法解决?


编辑:回复评论

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

2 个答案:

答案 0 :(得分: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]仍然值得了解 - 它对测试特别有用。

答案 1 :(得分: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();
    }
}

Online Demo

希望有所帮助。