Reputation: 57
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
Reputation: 73163
You're kind of after friend
keyword like in C++. And there is no equivalent in C# sadly.. I see some possibilities.
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.
}
Does A
matter only to S
? In that case you can make A
a private inner class to S
, as shown by Cloud9999Strife.
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
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
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
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
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