如何在C#类的Class级别而不是程序集级别实现类似C ++的朋友关系?

时间:2014-09-22 01:53:50

标签: c# oop

我知道有一个'内部' keyword和[InternalsVisibleTo]属性。但是如何允许不在同一程序集中的类级别的类来修改私有数据呢?也就是说,只允许程序集中的特定类访问私有数据,而不允许该程序集下的evey类?

我在此之前问过这个问题How to implement C++ like friend relationship in C#但是它不够具体,所以我在这里再问一遍。

此处讨论了与C#不使用朋友的理论和原因相关的其他讨论 Why does C# not provide the C++ style 'friend' keyword?

2 个答案:

答案 0 :(得分:0)

我已经考虑过了,我认为我有一个反思的解决方案。不确定这是一个很好的方法。

如果我有SomeClass有朋友FriendClass。 NotFriendClass和FriendClass在同一个程序集中,但只有FriendClass可以访问SomeClass的私有数据。以下是SomeClass的需求:

class SomeClass
{
    private bool isMyFriend()
    {
        StackTrace st = new StackTrace();

        StackFrame callerSF = st.GetFrame(2);

        MethodBase mb = callerSF.GetMethod();
        Type callerType = mb.DeclaringType;

        // FriendClass is my friend
        if (typeof(FriendClass) == callerType)
            return true;
        else
            return false;

    }
// ....

在这个方法中,SomeClass检查调用者类是否是他的朋友。是的,它有一个硬编码的if(typeof(FriendClass)== callerType)但是C ++的朋友也需要在声明中对类名进行硬编码。

对于那些"知道朋友" SomeClass的方法,应该是:

    public bool SetData(int x)
    {
        if (!isMyFriend())
           return false;

        this.privateData = x;
        return true;

    }

唯一的问题是运行时检查。但是,我认为使用friend将一些程序从C ++移植到C#已经足够了。

答案 1 :(得分:0)

我通常会将一个友好关键字似乎很方便的情况转换为一种模式,其中一个类本身确定哪些类可以实例化它,而不会破坏正确的OO设计。

想法是希望控制谁可以实例化的类,为允许获取它的实例的类提供友谊。例如,InControl类允许ChosenOne和ChosenTwo类获取InControl的实例。

“选择”类(即ChosenOne或ChosenTwo)可以“参与”InControl提供的友谊与其自身的实例(friendship.engage(this)),而友谊可以通过提供它来“接受”该实例InControl的一个实例(friend.accept(new InControl()))。

实际的友谊是通过嵌套的单个实例友谊类实现的。友谊仅限于使用通用IFriendship接口的“选定”类,其中T是选定的类。反过来,每个'选择'类需要实现通用的IFriendable接口,其中T为InControl,以便能够接收InControl的实例。

InControl类有一个私有构造函数,以避免除了朋友之外的任何人进行实例化:

public interface IFriendable<T>
{
    void accept(T friend);
}

public interface IFriendship<T>
{
    void engage(T friend);
}

public class ChosenOne : IFriendable<InControl>
{
    private InControl _friend { get; set; }

    private ChosenOne()
    {
        InControl.friendship.engage(this);
    }

    public void accept(InControl friend)
    {
        _friend = friend;
    }
}

public class ChosenTwo : IFriendable<InControl>
{
    private InControl _friend { get; set; }

    private ChosenTwo()
    {
        InControl.friendship.engage(this);
    }

    public void accept(InControl friend)
    {
        _friend = friend;
    }
}

public class InControl
{
    public interface IFriendship : IFriendship<ChosenOne>, IFriendship<ChosenTwo> { }

    public static IFriendship friendship { get { return Friendship.instance; } }

    private class Friendship : IFriendship
    {
        static Friendship()
        {
        }

        internal static readonly Friendship instance = new Friendship();

        public void engage(ChosenOne friend)
        {
            friend.accept(new InControl());
        }

        public void engage(ChosenTwo friend)
        {
            friend.accept(new InControl());
        }
    }

    private InControl()
    {
    }
}