0

I know there is a 'internal' keyword and [InternalsVisibleTo] attribute. But how to allow a class in class level that is not in the same assembly to modify private data in ? That is, only allow a particular class in an assembly to access private data but not evey classes under that assembly?

I asked this question before here How to implement C++ like friend relationship in C# but it is not specific enough, so I ask again here.

Other discussion related to the theory and reason why C# not use friend was discussed here Why does C# not provide the C++ style 'friend' keyword?

Community
  • 1
  • 1
palazzo train
  • 3,229
  • 1
  • 19
  • 40
  • 3
    Don't. Come up with a better design, who needs friends? It always was a sign of code stink in C++. As an alternate suggestion perhaps think about using Interfaces and explicit implementation http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx – Mick Sep 22 '14 at 01:58
  • @Mick Yes, I totally agree with you. But I am just thinking of it out of curiosity and for study/research purpose, just to see what we can get from C#. – palazzo train Sep 22 '14 at 02:04
  • I think Anders Hejlsberg would have been fully aware of all the constructs available in C++ and made deliberate decisions about how to help foster good practices in C#, and what constructs in C++ to remove from C# to eliminate in bad practices, the C++ friends construct probably fell into the latter category. It's a sign of badly over coupled design, when I said a better design I should have specified a less highly coupled design. Your answer below is an illustration of over coupling. – Mick Sep 22 '14 at 02:09
  • Well looks like you're flagging your own question as a dupe and the answer to that question you've linked to pretty much echoes what I've said above – Mick Sep 22 '14 at 02:13

2 Answers2

0

I have thought about it and I think I have a solution by reflection. Not sure is it a good way to do it.

If I have SomeClass that has a friend FriendClass. NotFriendClass and FriendClass are in the same assembly but only FriendClass can access SomeClass's private data. Here is what SomeClass needs:

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;

    }
// ....

In this method SomeClass checks whether the caller class is his friend. Yes it has a hardcoded if (typeof(FriendClass) == callerType) but C++ friend also need to hardcode the class name in the declaration.

For those "friend-awared" methods of SomeClass, it should be like:

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

        this.privateData = x;
        return true;

    }

The only problem is it is a run-time checking. But still, I think it is good enough for porting some program from C++ to C# using friend.

palazzo train
  • 3,229
  • 1
  • 19
  • 40
0

I usually translate cases where a friend keyword seems handy into a pattern where a class itself determines which classes can instantiate it, without breaking proper OO-design.

The idea is that the class that wants control over who can instantiate, offers friendship to the classes that are allowed to get an instance of it. For example, class InControl allows classes ChosenOne and ChosenTwo to obtain instances of InControl.

A 'chosen' class (i.e. ChosenOne or ChosenTwo) can 'engage' the friendship offered by InControl with an instance of itself (friendship.engage(this)) and the friendship in turn can 'accept' that instance by providing it an instance of InControl (friend.accept(new InControl())).

The actual friendship is implemented with a nested single instance Friendship class. Friendship is confined to 'chosen' classes using a generic IFriendship interface with T being a chosen class. In turn, each 'chosen' class needs to implement the generic IFriendable interface with T being InControl, to be able to receive an instance of InControl.

The InControl class has a private constructor to avoid instantiation by anyone else but friends:

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()
    {
    }
}