Ivan
Ivan

Reputation: 57

C# class members access issue

class A
{
    public int x { get; set; }
    // other properties
}

class S
{
    public A GetA(...);
}

I would like to make the following restriction:
A can be only modified inside S. When someone else get A via GetA(), he can only get A's properties, not modify them.

I decided to make a new function in S that returns another object:

class B
{
    A a;
    public int x { get { return a.x; } }
    // replicate other A's properties
}

Is there a better solution?

Upvotes: 4

Views: 120

Answers (5)

nawfal
nawfal

Reputation: 73163

You're kind of after friend keyword like in C++. And there is no equivalent in C# sadly.. I see some possibilities.

  1. Is S a kind of A? In that case inheritance can help you as you can restrict via protected modifier.

    class A
    {
        public int x { get; protected set; }
    }
    
    class S : A
    {
        public A GetA() { return this; } //or something like that.
    }
    
  2. Does A matter only to S? In that case you can make A a private inner class to S, as shown by Cloud9999Strife.

  3. Probably the best would be to leave A as an abstract class or interface for the public and have a concrete implementation of it nested in S - a combination of both Jamiec's and Cloud9999Strife's answer.

    public abstract class A // or interface
    {
       public abstract int x { get; }
    }
    
    class S
    {
        public A GetA() { return AImplInstead(); }
    
        class AImpl : A
        {
            // nothing stopping you having a setter on the class
            public override int x { get; set; } 
        }
    }
    

In essence, if A can be modified by one generic class, then it can be everywhere, unless there is a relation between A and S. That's how C# is designed. Now all we can do is make it (ie modifying A) difficult for end user. Exposing interface is one part of it. Nesting makes it one level deeper. Mind you, there is also reflection which will break all the encapsulation anyway..

Upvotes: 1

Markus
Markus

Reputation: 22436

You could change your implementation of A so that you introduce a readonly mode. After creating the instance, you can set the properties as you need. Once anyone gets the instance, you can enable the readonly mode. In readonly mode, the setters should throw an Exception, e.g. InvalidOperationException. In order to avoid a ton of if (IsReadOnly) ...; statements, you could use the Strategy pattern to change the behavior of the instance.
Depending on your needs, you can also create a copy with ReadOnly mode activated once a caller requests the object. This would allow for changes even after the instance was requested the first time.
An advantage of this over an interface is that a caller cannot use a cast to gain access to the setters. Though you do not have to implement an interface, the implementation of the readonly mode should be worth the effort. This depends on your analysis of risk and damage.

Upvotes: 0

Cloud9999Strife
Cloud9999Strife

Reputation: 3202

Perhaps a nested class could also meet your needs?

public class S
{
    private A ClassA { get; set; }

    public S()
    {
        ClassA = new A();
    }

    private class A
    {
        public int TestProperty { get; set; }
    }

    public int GetClassATestProperty
    {
        get
        {
            return this.ClassA.TestProperty;
        }
    }
}

Upvotes: 1

Jamiec
Jamiec

Reputation: 136074

You could make an interface of A with only the getters defined, and return that from GetA:

public interface IA
{
   int x { get; }
}

class A : IA
{
    public int x { get; set; } // nothing stopping you having a setter on the class
}

class S
{
    private A a = new A(); // you can call the setter internally on this instance
    public IA GetA(){ return a; } // when someone gets this thy only get the getter
}

Of course, there's nothing stopping someone casting the result of GetA to A and then they have the setter - but there's really nothing you can do about that!

Upvotes: 7

Rotem
Rotem

Reputation: 21917

Another option is to return a copy of A, not A itself.

class S
{
    public A GetA() { return a.Clone(); }
}

You could also make A a struct, which would cause this to happen automatically. This of course depends how big or complex A is. In most cases an interface might be better, as detailed in Jamiec's answer.

Upvotes: -1

Related Questions