1

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()");
        }
    }
kkirkfield
  • 85
  • 8
  • 1
    Why not define BaseClass outside of ManagerClass and derive from it for the encapsulated class? – dartonw Mar 22 '15 at 17:41
  • Embedding is not a substitute for `friend`. – H H Mar 22 '15 at 17:46
  • 1
    Here's a related question with some ideas of how to achieve this http://stackoverflow.com/q/1664793/1316346 – Kevin DiTraglia Mar 22 '15 at 17:48
  • @dartonw If I define the BaseClass outside the ManagerClass, then there would be no way to allow only the ManagerClass to call certain methods on the BaseClass. – kkirkfield Mar 22 '15 at 18:38
  • @KevinDiTraglia Thanks for the link, it looks like my best options are delegates or interfaces. – kkirkfield Mar 22 '15 at 18:39
  • Yeah I've noticed at least 1 problem with my solution posted, it tries to new up an abstract class, still thinking through how to get around that, but it will probably end up being too convoluted for human understanding. That won't stop me though! – Kevin DiTraglia Mar 22 '15 at 18:41

3 Answers3

1

So I think something like this could work. It got a little convoluted as I wrote it, some of it may be able to be cleaned up, but basically hinges on the ability of the embedded class to access private static variables of the the parent. I haven't got a chance to test it, but I think this should achieve what you want:

public class ManagerClass
{
    // This is a singleton class.
    static ManagerClass instance;
    private static Func<BaseClassFunctionHolder, BaseClass> _createBaseClass;

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

        //example
        BaseClassFunctionHolder holder = new BaseClassFunctionHolder();
        T baseClass = _createBaseClass(holder);

        //access to baseClass methods through holder.DoStuff, and holder.DoOtherStuff
        return baseClass;
    }

    private class BaseClassFunctionHolder
    {
        public Action DoStuff { get; set; }
        public Action DoOtherStuff { get; set; }
    }

    abstract class BaseClass
    {
        static BaseClass()
        {
            _createBaseClass = (holder) => new BaseClass(holder);
        }

        private BaseClass(BaseClassFunctionHolder holder)
        {
            holder.DoStuff = DoStuff;
            holder.DoOtherStuff = DoOtherStuff;
        }

        public bool IsRunning { get; set; }

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

        abstract void DoOtherStuff();
    }
}

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

    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
    }
}
Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
  • I'll have to play around with it some before I fully understand it, but it looks like it will work at first glance. I didn't know that public members inside of a private class are accessible in the encapsulating class. That is really useful. Thanks for the quick reply. I'll let you know how it goes in a few. – kkirkfield Mar 22 '15 at 18:42
  • Upon further examination, this line isn't going to work `createBaseClass = (holder) => new BaseClass(holder);`. It's going to have to instantiate the correct child class, which makes it a harder problem. Still thinking through if it's possible. – Kevin DiTraglia Mar 22 '15 at 18:44
  • In addition to you pointing out that it is trying to instantiate an abstract class, how is the _createBaseClass delegate being set? It looks like it is being set in the static constructor of the BaseClass, but when does that get called? The first time an object of that class is created? So you are trying to call _createBaseClass before it has been set? – kkirkfield Mar 22 '15 at 18:46
  • static constructors are guaranteed to be run before anything using that class for anything is done https://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx – Kevin DiTraglia Mar 22 '15 at 18:48
  • I get that, but if CreateBaseClass() gets called before a BaseClass has been used, then _createBaseClass is null because the static BaseClass constructor has not been called yet. Or am I missing something? – kkirkfield Mar 22 '15 at 18:57
  • I didn't think so, but I just tried it and it does indeed throw a null reference(I figured the reference to BaseClass being in the generic definition would be enough to cause it to be called). I suppose to fix that you could have some silly Initialize call on BaseClass that can essentially do nothing be called before-hand, or any interaction with the class what-so-ever, but this solution is getting so hacky I can't really recommend it =P. – Kevin DiTraglia Mar 22 '15 at 19:08
  • The initializer is fine, since I might have to do other static initialization code in the BaseClass anyway. The link you posted shows a good example. I'm thinking maybe have the BaseClass constructor take a generic type parameter that derives from itself, and then when the constructor is called from the delegate in the CreateBaseClass() method it calls the right constructor? – kkirkfield Mar 22 '15 at 19:14
  • As long as I'm not using reflection I'm okay with it. Reflection would be too slow for my goals generally. – kkirkfield Mar 22 '15 at 19:16
  • Yeah I'm trying to come up with a solution that doesn't use reflection. I haven't come up with anything yet, let me know if you get something working because I'm curious now. – Kevin DiTraglia Mar 22 '15 at 19:17
  • Also note, I cannot make my ManagerClass static because it is deriving from other classes in my project. I just removed as much as possible to make it simpler. – kkirkfield Mar 22 '15 at 19:31
  • OK I give up, I can't think of any reasonable way to do this =(. Would you like me to delete this answer to give your question more traffic, or do you think this discussion thread is worth keeping it alive? – Kevin DiTraglia Mar 22 '15 at 19:56
  • Here is what I'm looking into right now. http://stackoverflow.com/questions/16796279/add-type-parameter-constraint-to-prevent-abstract-classes/16798058#16798058 I think this is worth keeping alive since I'm still looking for a solution. This delegate method is getting more and more ugly though. I might end up doing interfaces. – kkirkfield Mar 22 '15 at 20:26
  • http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx I've determined that the delegate solution will not work, because I need to store the generic in the delegate and there is no way to get the types to match up that way. So now I'm trying interfaces. – kkirkfield Mar 22 '15 at 21:03
  • Yeah I went through all the same stuff just now, and just threw in the towel eventually =(. Would be nice if c# would allow a delegate with an open generic type, but it is not supported. – Kevin DiTraglia Mar 22 '15 at 21:06
0

I'm still looking for a way to do this without reflection, so I'll leave this question unanswered until someone can figure it out. For now, here is a solution that uses reflection.

    using System;
    using System.Collections.Generic;
    using System.Reflection;

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

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

            // Is not accessible from outside ManagerClass
            // dc.DoStuff();

            ManagerClass.TestManager();

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

    class ManagerClass
    {
        static ManagerClass instance;
        static List<BaseClass> managedList = new List<BaseClass>();
        static MethodInfo doStuffMethod = typeof(BaseClass).GetMethod("DoStuff", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding);
        static MethodInfo doOtherStuffMethod = typeof(BaseClass).GetMethod("DoOtherStuff", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding);

        public static ManagerClass Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ManagerClass();
                }

                return instance;
            }
        }

        public static void TestManager()
        {
            for (int i = 0; i < managedList.Count; i++)
            {
                // DoStuff() and DoOtherStuff need to be accessible only from the ManagerClass.
                // Invocation of the virtual methods calls the derived methods.
                doStuffMethod.Invoke(managedList[i], null);
                doOtherStuffMethod.Invoke(managedList[i], null);
            }
        }

        public abstract class BaseClass
        {
            public BaseClass()
            {
                Console.WriteLine("BaseClass()");
                managedList.Add(this);

                // All of ManagerClass fields are accessible from here:
                // instance, managedList, etc.
            }

            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()");

            // None of the ManagerClass fields are accessible from classes deriving from BaseClass:
            // instance, managedList, etc.
        }

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

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

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

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

        protected override void DoOtherStuff()
        {
            Console.WriteLine("AnotherDerivedClass.DoOtherStuff()");
        }
    }
kkirkfield
  • 85
  • 8
0

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()");
        }
    }
kkirkfield
  • 85
  • 8