Saturday, May 17, 2008

Using Friends with C#, or should I say Bully



One feature that is missing in C# is the use of Friends, something you can do with C++. There are valid reasons why C# doesn't really support friends, mostly because it breaks OO design. However it would be nice to have something similar. .NET offers the use of internal keyword, which does provide some features similar to friend but it is not the same.

I wanted to do something simple, having a class that can set the private data of another class. Without the use of Reflection, I noticed this was not possible, so I tried to do the next best thing. Having a class able to set the value of a protected item within another class, but without directly inherting from it. To solve the issue, I decided to use a combination of inheritance and nested private classes. Below is an example:




public class Bob

{

private string mMyBestFriend;



public string Friend

{

get { return mMyBestFriend; }

protected set { mMyBestFriend = value; }

}

}



public class Mike

{

public Mike()

{



}



public void MakeMeAFriendOfBob(Bob bob)

{

// use the inner class to access the name field.

new SetFriend().SetFriendName(bob, "Mike");

}



private class SetFriend : Bob

{

public SetFriend()

{



}



public void SetFriendName(Bob bob, string name)

{

bob.Friend = name;

}



}

}


Notice in the example above, that the class Mike is able to set a protected field within the class Bob. Mike is now a friend of Bob in a way. I used an inner private class to handle the inheritance so the class Mike does not need to inherit from Bob directly. There is a reason I said this is like Mike is a Bully, because Bob never really specified who are its friends, as you can do in C++, using simple inheritance I forced Mike to bully its way into Bob.


The main program can do somthing like this:




Mike me = new Mike();

Bob bob = new Bob();



me.MakeMeAFriendOfBob(bob);



Below is another example, allowing one class to set the value of a static field within another class. The static field however is not public, so only certain classes can set it. Same concept as above.




public class Registry

{

private static string mValue;



// only certain classes can set the value

protected string RegisterValue

{

set

{

mValue = value;

}

}



// other classes can read the vlaue

public static string RegisteredValue

{

get { return mValue; }

}

}





// notice that FriendOfRegistry does not inhert directly from Registry

public class FriendOfRegistry

{



public FriendOfRegistry()

{

// at this point I would like to register my value

new RegisterValueSetter("SpecialCode");

}





private class RegisterValueSetter : Registry

{

public RegisterValueSetter(string value)

{

base.RegisterValue = value;

}

}

}





A few notes



  • This is not a perfect solution, we can always set values to protected items, we just need to use inheritance, so we are not really forcing who can be friends with who, we are just making it a little harder, so the general population can see someone's privates :), sorry I mean protected data.

  • Using reflection you can always set a private field, but that just evil.

  • I used this concept when I wanted to register a value within a class, and make sure only a certain class can do it. the idea is that if someone notices a field to be protected, they know they should not really mess with it, unless they have a good reason.

  • Overall, this is not really how to make "Friend work", and that's why I consider this method more like "Bully".

    If you can think of other ideas to solve the Friend feature within C#, I would like to hear from you.

Happy .Netting.

4 comments:

Administrator said...

Sir your explaination is so nice.Could you please first expalin "interfaces" and where they are used with your simple sxamples.thanks.

jrnail23 said...
This comment has been removed by a blog administrator.
Mike Peretz said...

All the code has been compiled with VS2008, using .NET 3.5

Anonymous said...

Your firs example wont compile, On my project I get error:

Cannot access protected member 'ConsoleApplication228.Bob.Friend' via a qualifier of type 'ConsoleApplication228.Bob'; the qualifier must be of type 'ConsoleApplication228.Mike.SetFriend' (or derived from it).

Do you not get the same error? I am using Visual Studio 2008 and compiling against .Net 2 and .Net 3.5 and both failed.

Thanks.