Search code examples
c#design-patternsaccess-modifiers

C# Public field that can be updated only from within the class


I have a C# class with a field that needs to be visible from outside the class, but should be changed only from within the class. However, the problem is that the field is updated via a public mutator method, not directly (or by a property):

public class FirstClass
{
    public SecondClass TheField {get; private set;}

    public FirstClass()
    {
        TheField = new SecondClass();
    }

    public void SomeMethod()
    {
        // ...
        TheField.Update(/*parameters*/);
        // ...
    }
}

public class SecondClass
{
    // some fields  

    public SecondClass() { }

    public void Update(/*parameters*/)
    {       
        // do something
    }
}

In other words, I would like the method Update() to be accessible only from within FirstClass.

Few possible solutions and the reasons why I'm not safisfied with them:

  1. Change SecondClass with a setter instead of a method. - Wouldn't work because I need to have parameters and a lot of stuff to do.
  2. Make Update() internal instead of public. - Still accessible from within the assembly, not good enough.
  3. Modify SecondClass from the FirstClass, ie. move the method Update() to FirstClass, make it private and expose all needed fields from SecondClass. - doesn't feel very object-oriented.
  4. Call Update() from SecondClass constuctor(s) and make a new instance of SecondClass every time I need to update it. - The performance will suffer because there is some stuff in SecondClass that never changes and I would want to process it every time I call Update().

Is there a way to do this?


Solution

  • A basic implementation of @aquaraga's answer, but using interfaces instead:

    public interface ISecond
    {
        // some properties  
    }
    
    public class FirstClass
    {
        private readonly SecondClass _TheField;
        public ISecond TheField { get { return _TheField; } }
    
        public FirstClass()
        {
            _TheField = new SecondClass();
        }
    
        public void SomeMethod()
        {
            // ...
            _TheField.Update(/*parameters*/);
            // ...
        }
    
        private class SecondClass : ISecond
        {
            // some properties  
    
            public void Update(/*parameters*/)
            {       
                // do something
            }
        }
    }
    

    Essentially, expose a public interface that has all the accessible members but no Update method. Employ a private nested class which has an Update method, but otherwise not accessible to calling code and expose it as the interface, not as the class.

    Some sample usage:

    FirstClass myFirst = ...
    myFirst.SomeMethod(); //updates ok!
    Console.WriteLine(myFirst.TheField.PropertyA); //can access properties of ISecond
    myFirst.TheField.Update(); //compiler error!
    

    One point is that you mention using "some fields"; as an interface you wouldn't be able to have fields but properties instead. I'm not sure if that's a deal breaker or not for you.

    EDIT: You mentioned your intent to reuse the SecondClass and minimize code duplication. One quick fix might be to declare some abstract class, a protected Update method, employ an internal constructor (so other assemblies can't inherit from it), then expose the Update call with a minimal implementation:

    public abstract class SecondClassBase : ISecond
    {
        // some properties
    
        internal SecondClassBase()
        {
    
        }
    
        protected void Update(/*parameters*/)
        {
            // do something
        }
    }
    

    Then in your FirstClass, nothing changes except the nested class:

    public class FirstClass
    {
        private readonly SecondClass _TheField;
        public ISecond TheField { get { return _TheField; } }
    
        public FirstClass()
        {
            _TheField = new SecondClass();
        }
    
        public void SomeMethod()
        {
            // ...
            _TheField.Update(/*parameters*/);
            // ...
        }
    
        private class SecondClass : SecondClassBase
        {
            public new void Update(/*parameters*/)
            {       
                base.Update(/*parameters*/);
            }
        }
    }
    

    Still some code duplication, but all you need to do now is copy/paste the code; no need to redeclare the properties or the Update logic for each implementation of FirstClass. You could also rename the new Update method to something else to avoid method hiding, but I figured I'd keep the same calling signature you had before.