Search code examples
c#scopeencapsulationderived-class

How can I derive classes from a base class that is encapsulated in a manager class?


I have a ManagerClass that manages classes that derive from an abstract BaseClass. I need it so that only the ManageClass can access certain methods on the BaseClass. I also need certain methods to be accessible outside the scope of the ManagerClass. Since C# doesn't have friends, I'm encapsulating the BaseClass inside the ManagerClass.

The problem:

  • BaseClass is not accessible to derive from
  • Making BaseClass public,means allowing DoStuff() and DoOtherStuff() to be called from outside the scope of ManagerClass, which I don't want.

How can I make this work?

    public class ManagerClass
    {
        // This is a singleton class.
        static ManagerClass instance;

        public static T CreateBaseClass<T>() where T : BaseClass, new()
        {
            // Create and return a BaseClass.
            // Everything in BaseClass should be accessible here.
        }

        abstract class BaseClass()
        {
            public bool IsRunning { get; set; }

            virtual void DoStuff()
            {
                // Do stuff.
            }

            abstract void DoOtherStuff();
        }
    }

    public class DerivedClass : ManagerClass.BaseClass
    {
        public override void DoStuff()
        {
            // Do stuff.
        }

        public override void DoOtherStuff()
        {
            // Do other stuff.
        }
    }

    class TestClass
    {
        static void Main(string[] args)
        {
            // Assume singleton is already created here.
            BaseClass bc = ManagerClass.CreateBaseClass<DerivedClass>();
            // bc.IsRunning should be accessible
            // bc.DoStuff() and DoOtherStuff() should not be accessible
        }
    }

**

Update

**

Okay, so after finding out there was no way to make the delegate work for the abstract class using generics, I tried using interfaces with a factory. This didn't work either, because I was forced to either make the entire BaseClass public, or be unable to call the DoStuff() and DoOtherStuff() from the ManagerClass. And then I realized I don't need a factory at all, because the DerivedClass calls the BaseClass constructor, and I can do all my stuff in there... sort of.

So at the moment I have a wrapper class that contains a BaseClass and a scope class that I can use to store delegates or other members that only the ManagerClass should have access to. The public members can still be accessed publicly, but the ManagerClass now has to go through the wrapper to access the methods.

New Problem

Now the only problem is that I'm storing a wrapper for each instance of BaseClass. Since I only need to store delegates in my BaseClassScope, how can I store the delegate when the BaseClass static constructor is called, and then how can I call the most overriden method using that delegate?

    using System;
    using System.Collections.Generic;

    class Program
    {
        static void Main(string[] args)
        {
            ManagerClass.BaseClass[] dc = new DerivedClass[4];

            for (int i = 0; i < 4; i++)
            {
                dc[i] = new DerivedClass();

                // Is accessible from outside ManagerClass
                dc[i].IsRunning = true;

                // Is not accessible from outside ManagerClass
                // dc[i].DoStuff();
            }

            ManagerClass.TestManager();

            // Wait for input.
            Console.ReadKey(true);
        }
    }

    class ManagerClass
    {
        static List<BaseClassWrapper> managedList = new List<BaseClassWrapper>();

        public static void TestManager()
        {
            for (int i = 0; i < managedList.Count; i++)
            {
                // Is accessible from inside ManagerClass
                managedList[i].bcs.DoStuff();
                managedList[i].bcs.DoOtherStuff();
            }
        }

        class BaseClassScope
        {
            public Action DoStuff;
            public Action DoOtherStuff;

            public BaseClassScope(Action ds, Action dos)
            {
                DoStuff = ds;
                DoOtherStuff = dos;
            }
        }

        class BaseClassWrapper
        {
            public BaseClass bc;
            public BaseClassScope bcs;

            public BaseClassWrapper(BaseClass bc, BaseClassScope bcs)
            {
                this.bc = bc;
                this.bcs = bcs;
            }
        }

        public abstract class BaseClass
        {
            public BaseClass()
            {
                Console.WriteLine("BaseClass()");
                var bcs = new BaseClassScope(DoStuff, DoOtherStuff);
                var bcw = new BaseClassWrapper(this, bcs);
                managedList.Add(bcw);
            }

            public bool IsRunning { get; set; }

            protected virtual void DoStuff()
            {
                Console.WriteLine("BaseClass.DoStuff()");
            }

            protected abstract void DoOtherStuff();
        }
    }

    class DerivedClass : ManagerClass.BaseClass
    {
        public DerivedClass()
        {
            Console.WriteLine("DerivedClass()");
        }

        protected override void DoStuff()
        {
            Console.WriteLine("DerivedClass.DoStuff()");
        }

        protected override void DoOtherStuff()
        {
            Console.WriteLine("DerivedClass.DoOtherStuff()");
        }
    }

Solution

  • I think this is going to be the solution I go with. It uses the curiously recurring template pattern, but it removes the need of all those wrapper classes, AND it doesn't use any reflection.

        using System;
        using System.Collections.Generic;
    
        namespace testapp2
        {
            class Program
            {
                static void Main()
                {
                    ClassA a = ClassA.Instance;
                    ClassB b = ClassB.Instance;
    
                    ManagerClass.TestManager();
    
                    Console.ReadKey(true);
                }
            }
        }
    
        class ManagerClass
        {
            static ManagerClass instance;
            static Dictionary<Type, ManagedClass> managedList = new Dictionary<Type, ManagedClass>();
    
            public ManagerClass Instance
            {
                get
                {
                    if (instance == null)
                    {
                        instance = new ManagerClass();
                    }
    
                    return instance;
                }
            }
    
            ManagerClass()
            {
    
            }
    
            public static void TestManager()
            {
                foreach (var kvp in managedList)
                {
                    kvp.Value.doStuffCallback();
                    kvp.Value.doOtherStuffCallback();
                }
            }
    
            public static void CreateManagedClass(Type type, Action doStuffCallback, Action doOtherStuffCallback)
            {
                managedList.Add(type, new ManagedClass(doStuffCallback, doOtherStuffCallback));
            }
    
            public static void DestroyManagedClass(Type type)
            {
                managedList.Remove(type);
            }
    
            class ManagedClass
            {
                public Action doStuffCallback;
                public Action doOtherStuffCallback;
    
                public ManagedClass(Action doStuffCallback, Action doOtherStuffCallback)
                {
                    this.doStuffCallback = doStuffCallback;
                    this.doOtherStuffCallback = doOtherStuffCallback;
                }
            }
    
            public abstract class ManagedClassBase<T> where T : class, new()
            {
                static T instance;
    
                public static T Instance
                {
                    get
                    {
                        if (instance == null)
                        {
                            instance = new T();
                        }
    
                        return instance;
                    }
                }
    
                protected ManagedClassBase()
                {
                    CreateManagedClass(typeof(T), DoStuff, DoOtherStuff);
                }
    
                ~ManagedClassBase()
                {
                    instance = null;
                }
    
                protected abstract void DoStuff();
                protected abstract void DoOtherStuff();
            }
        }
    
        class ClassA : ManagerClass.ManagedClassBase<ClassA>
        {
            protected override void DoStuff()
            {
                Console.WriteLine("ClassA.DoStuff()");
            }
    
            protected override void DoOtherStuff()
            {
                Console.WriteLine("ClassA.DoOtherStuff()");
            }
        }
    
        class ClassB : ManagerClass.ManagedClassBase<ClassB>
        {
            protected override void DoStuff()
            {
                Console.WriteLine("ClassB.DoStuff()");
            }
    
            protected override void DoOtherStuff()
            {
                Console.WriteLine("ClassB.DoOtherStuff()");
            }
        }