Consider the following code pattern:
// Each foo keeps a reference to its manager
class Foo
{
private FooManager m_manager;
}
// Manager keeps a list of all foos
class FooManager
{
private List<Foo> m_foos;
}
Problem: there is no way to create a new Foo and update both m_foos list in the FooManager, and m_manager reference in the new Foo instance without exposing some privates publicly (and running the risk of someone desyncing the list with actual Foos).
E.g. one could implement a constructor Foo(FooManager manager) in Foo. It could set m_manager reference, but it has no way to access the m_foos list. Or you could implement CreateFoo() method in the manager. It can access m_foos list, but it has no way to set m_manager in Foo.
In C++, one would obviously declare FooManager a friend of Foo to express the design intent, but this is not possible in C#. I also know that I could make Foo an inner class of FooManager to gain access, but this is not a solution either (what if Foo could belong to more than one manager class?)
Btw. I know about "internal" access in .NET, but it requires that Foo and FooManager live on their own in a separate assembly, which is not acceptable.
Any workarounds for that without making private stuff public?
A friend class is used when we need to access private and protected members of the class in which it has been declared as a friend. It is also possible to declare only one member function of another class to be a friend.
A friend function can be friendly to 2 or more classes. The friend function does not belong to any class, so it can be used to access private data of two or more classes as in the following example. The friend functions can serve, for example, to conduct operations between two different classes.
A friend function is not invoked using the class object as it is not in the scope of the class. A friend function cannot access the private and protected data members of the class directly. It needs to make use of a class object and then access the members using the dot operator.
The most of OOP experts dislike friend function simply because they want the OOP part of C++ to behave like Smalltalk. But C++ is not Smalltalk, and they cannot even understand that friend don't break encapsulation, for the very simple reason that a function cannot be friend of your class without your class wants it.
If I understand all correctly:
public abstract class FooBus
{
protected static FooBus m_bus;
}
public sealed class Foo : FooBus
{
private FooManager m_manager;
public Foo(FooManager fm)
{
if (fm == null)
{
throw new ArgumentNullException("Use FooManager.CreateFoo()");
}
if (m_bus != fm)
{
throw new ArgumentException("Use FooManager.CreateFoo()");
}
m_manager = fm;
}
}
public class FooManager : FooBus
{
private List<Foo> m_foos = new List<Foo>();
public Foo CreateFoo()
{
m_bus = this;
Foo f = new Foo(this);
m_foos.Add(f);
m_bus = null;
return f;
}
}
One option would be to use a private nested class for Foo that implements a public interface:
public interface IFoo
{
// Foo's interface
}
public sealed class FooManager
{
private readonly List<Foo> _foos = new List<Foo>();
public IFoo CreateFoo()
{
var foo = new Foo(this);
_foos.Add(foo);
return foo;
}
private class Foo : IFoo
{
private readonly FooManager _manager;
public Foo(FooManager manager)
{
_manager = manager;
}
}
}
As the Foo class is a private nested class, it can't be created outside the FooManager, and so FooManager's CreateFoo()
method ensures that everything stays in-sync.
What you can do is create your classes inside a different kind of namespace, let's call it a "module" (don't be fooled by the class keyword, this is not a real class):
public static partial class FooModule {
// not visible outside this "module"
private interface IFooSink {
void Add(Foo foo);
}
public class Foo {
private FooManager m_manager;
public Foo(FooManager manager) {
((IFooSink)manager).Add(this);
m_manager = manager;
}
}
public class FooManager : IFooSink {
private List<Foo> m_foos = new List<Foo>();
void IFooSink.Add(Foo foo) {
m_foos.Add(foo);
}
}
}
Since the "module" is a partial class, you can still create other members inside it in other files in the same compilation unit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With