4

Is there an access modifier, or combination thereof, to restrict access to an outer class only?

For the Position property of nested class PanelFragment below, I would like only the containing class ViewPagerPanels to be able to set it (via the setter, I realize this could be done through a constructor parameter also).

public class ParcelView : MXActivityView<ParcelVM>, ViewPager.IOnPageChangeListener, IFragmentToViewPagerEvent
{
    private ViewPagerPanels _pagerPanels;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        _pagerPanels = new ViewPagerPanels(5);  // 5: magic number, put int constant

        _pagerPanels[0] = new ViewPagerPanels.PanelFragment(typeof(ViewA));
        // ...
    }

    private class ViewPagerPanels
    {
        public class PanelFragment
        {
            public Fragment Fragment { get; set; }
            // ?? - access modifer for set
            public int Position { get; private set; }
        }

        public readonly int PANEL_COUNT;

        private PanelFragment[] _panels;

        public ViewPagerPanels(int count)
        {
            PANEL_COUNT = count;
            _panels = new PanelFragment[PANEL_COUNT];
        }

        public PanelFragment this[int i]
        {
            get
            {
                return _panels[i];
            }

            set
            {
                _panels[i] = value;
                // !! - cannot access private property
                _panels[i].Position = i;
            }
        }
    }
}
7
  • If you don't mind some namespace pollution you can work something out with internal Commented Sep 28, 2018 at 16:06
  • @BurnsBA I'm attempting to restrict access, internal gives public access to the containing assembly, correct? Commented Sep 28, 2018 at 16:09
  • Yes, It's public to the containing assembly. See learn.microsoft.com/en-us/dotnet/csharp/language-reference/… Commented Sep 28, 2018 at 16:11
  • 1
    I'm confused by the question. PanelFragment is a public member of private class ViewPagerPanels, so the only code that can access PanelFragments at all is a member of ParcelView, which you wrote. It sounds like you are asking "how do I stop myself from writing code I don't want to write?" There is no compiler setting for that. If you don't want any member of ParcelView other than ViewPagerPanels to access PanelFragment, then don't write the code that does that! It's all your code! Commented Sep 28, 2018 at 18:15
  • 1
    If someone else ends up working on it then let that person make good decisions about how to use private implementation details. "Someone might write bad code in the future" is a good reason to make your code clear, well-documented, and well-organized. It's a good reason to be very careful about the design of extension points like virtual methods, protected fields, and so on. But all the time I see questions on SO that boil down to "how do I prevent myself / my coworkers from writing bad code?" That's a job for a strong culture of code reviews, not for more kinds of access modifiers. Commented Sep 28, 2018 at 18:54

3 Answers 3

3

No, it's not possible to do it directly. The most restrictive access modifier, private, already allows access from within the same class. Every other modifier further expands that access.

Every class, no matter if its nested, private or public, always has access to every single of its own declared members, with no chance of applyng restrictions to itself. The closest we can get is by using a readonly field (or a getter only property) that prevents the declaring class from modifying a variable outside the constructor. But for a read-write one, we're out of options.

Sign up to request clarification or add additional context in comments.

1 Comment

So no "nested private" modifier? Well it's good to know that I can at least now end my search, thanks.
1

There is a solution for this type of protection scenarios. But you should do the following changes;

1- Replace you concrete class with an interface or abstract class and expose this to outside world

2- Implement this interface with a concrete class

3- Control the creation of this class by a factory method

4- Set the property by casting the interface (or abstract class) to your private class type

Sample code changes

public interface IPanelFragment
{
        Fragment Fragment { get; set; }
        // ?? - access modifer for set
        int Position { get; }
}

class PanelFragment : IPanelFragment
{
      public Fragment Fragment { get; set; }
      // ?? - access modifer for set
      public int Position { get; set; }
}

private IPanelFragment[] _panels;

public IPanelFragment CreateFragment(Fragment fragment, int pos)
{
     return new PanelFragment() { Fragment= fragment, Position = pos };
}

public IPanelFragment this[int i]
{
      get
      {
          return _panels[i];
      }

      set
      {
           _panels[i] = value;
           // !! - cannot access private property
           ((PanelFragment)_panels[i]).Position = i;
       }
  } 

Comments

0

A possible workaround

public int Position { get; private set; }
public int InitPosition { set { Position = value; } }

or, depending on your philosophical perspective concerning getter-less Properties

public void InitPosition(int value) { Position = value; }

4 Comments

But that still lets the nested class to set the value, in addition to the container class.
@Alejandro Position can now only be set through InitPosition, and not by an accidental/unintentional use of Position (by the container class or otherwise). I could go as far as renaming them to ReadPosition and WritePosition, but think this should suffice for now (I think this code's getting scrapped for a list anyway).
Doesn't that just moves the problem to a different property/method? InitPosition can still be called from the container class in addition to the parent one. Just though a different name.
@Alejandro It does, but it's a bit more explicit by adding a "layer" to help prevent misuse, since I don't have the compiler on my side, and would rather not throw an exception at runtime (which would be especially insidious I think).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.