2

Let's say I have a Screen class. This class has few abstract methods like EnterScreen, ExitScreen that are protected.

I want to change screen only through a ScreenManager class but I cannot since both methods are protected.

If I make methods public, I can make a call to both methods in ScreenManager but then I expose them to other classes that accept Screen class as a parameter thus they can easily call Enter and Exit Screen.

Any idea of how can make a call only through ScreenManager and without exposing both methods to other classes? (Only ScreenManager can change screens)

Thanks in advance.

EDIT: @derHugo provided an answer that I should use namespaces and internal keyword, however, I've already tried this but it's not working as expected (methods of the internal class are still accessible in the namespace that is not the same as the Screen class). I'll provide code below and behavior I'm getting.

namespace Test
{
    public abstract class Screen<T> 
    {    
        internal abstract void EnterScreen();

        internal abstract void ExitScreen();
    }
}

// Seperate class

namespace Test
{    
    public class SimpleScreen : Screen<UnityEngine.GameObject>
    {
        internal override void EnterScreen() { }

        internal override void ExitScreen() { }
    }
}

// This DOESN'T HAVE A NAMESPACE but I can STILL access the internal methods of the Screen class.

public class GameManager : MonoBehaviour, IInitializable<SimpleScreen>
{
    public void Initialize(SimpleScreen screen)
    {
        //   I CAN REFERENCE .Enter & ExitScreen methods here 
    }    
}
CSDev
  • 3,177
  • 6
  • 19
  • 37
rootpanthera
  • 2,731
  • 10
  • 33
  • 65
  • I stand corrected but I think it's kind of the closest you can get unless you use nested private classes. Even if methods are `private` you can still access them using [Reflection](https://stackoverflow.com/questions/920844/how-can-i-access-an-internal-class-from-an-external-assembly) – derHugo Jul 25 '19 at 19:57
  • The `internal` keyword is an access modifier that's based on the _assembly_, not the _namespace_. In Unity, all of your scripts are compiled into two assemblies: Everything in an Editor directory, then everything else. If you want to use the `internal` keyword, you need to write that C# outside of Unity, compile it into a DLL, and place that DLL into your project. – Foggzie Jul 25 '19 at 21:36
  • @rootpanthera did you find an answer to this question that worked for you? – Ruzihm Jul 31 '19 at 19:30

2 Answers2

1

One way to approach it is to inject your screenManager instance into your Screen and pass it these Actions that it has privileged access to:

public abstract class Screen<T> 
{    
    protected abstract Action GetEnterScreenAction();
    protected abstract Action GetExitScreenAction();

    public Screen(ScreenManagerInterface screenManager)
    {
        screenManager.SetScreenActions(GetEnterScreenAction(), GetExitScreenAction());
    }
}


public class SimpleScreen : Screen<UnityEngine.GameObject>
{
    private void EnterScreen() { }

    private void ExitScreen() { }

    protected override Action GetEnterScreenAction() { return EnterScreen;}
    protected override Action GetExitScreenAction() { return ExitScreen;}

    public SimpleScreen(ScreenManagerInterface screenManager, ....) : base(screenManager) { }
}

public interface ScreenManagerInterface
{
    void SetScreenActions(Action enterScreenAction, Action exitScreenAction);
}
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
0

A need to make some public methods available only to some other classes may indicate a design flaw in classes relationship. Maybe moving part of the responsibilities into ScreenManager itself could solve this problem.

As you declare Screen class abstract you intent on public or protected methods that can be overridden. So the only option for you to hide them from other classes is to make them protected.

And the only way to access protected members is from the class itself, derived or nested type.

You can make the class ScreenManger nested inside your Screen and it will default to private or make it public. If I understood your question correctly I made an example but without Unity.

I hope it can help

public interface IInitializable<T>
{
}

public class SimpleScreen : Screen<object>
{
    protected override void EnterScreen() { }

    protected override void ExitScreen() { }

    public class ScreenManager
    {
        private SimpleScreen _simpleScreen;

        public void Awake()
        {
            _simpleScreen.EnterScreen();
        }
    }

}

public abstract class Screen<T>
{

    protected abstract void EnterScreen();

    protected abstract void ExitScreen();
}

public class GameManager : IInitializable<SimpleScreen>
{

    public void Initialize(SimpleScreen screen)
    {
        var screenManager = new SimpleScreen.ScreenManager();
        screenManager.Awake();

        screen.
    }
}