Dan
Dan

Reputation: 2028

How can I expose private objects in a class in a read only manner?

Let's say I have a simple class which contains a bunch of attributes or properties:

internal class ConnectionProperties
{
    internal string Name = "Default Name";
    internal bool Enabled = false;
}

And these are used within a class called Data:

internal class Data
{
    private ConnectionProperties _connection;

    internal Data()
    {
        this._connection = new ConnectionProperties();

        // Business logic to configure connection
        this._connection.Name = "My Connection";
        this._connection.Enabled = true; _
    }

}

How can I allow external access to the properties and values of _connection WITHOUT allowing them to be changed from anywhere apart from the class Data.

I know I could add a property such as

internal ConnectionName => _connection.Name;

But that feels very messy and could be tough to maintain

Upvotes: 3

Views: 551

Answers (3)

panoskarajohn
panoskarajohn

Reputation: 1988

You can have read only properties inside your class like this with different levels of encapsulation.

public string Name{get;} With this you are only able to set the Name only inside the constructor of ConnectionProperties class.

public string Name{get;private set;} With this you are only able to set the Name anywhere inside the ConnectionProperties class and not anywhere else.

public string Name{get;internal set;} With this you are only able to set the Name anywhere inside the dll not allowing other dlls to set its property.

It all starts from your ConnectionProperties on how other classes view it.

Here is how I would solve your example.

public class Program
    {
        public static void Main(string[] args)
        {
            var data = new Data();
            var c = new ConnectionProperties("", false);
            data.Connection.Name = ""; // prints readonly error
            data.Connection = new ConnectionProperties("", false); //prints readonly error
            data.Connection = c; //prints readonly error
        }
    }

    internal class ConnectionProperties
    {
        internal ConnectionProperties(string name, bool enabled)
        {
            Name = name;
            Enabled = enabled;
        }
        internal string Name { get; } 
        internal bool Enabled { get; }
    }
    internal class Data
    {
        public ConnectionProperties Connection => new ConnectionProperties("My Connection", true);

        internal Data()
        {
        }
    }

Upvotes: 1

V0ldek
V0ldek

Reputation: 10563

If you control the ConnectionProperties type, you can make it implement an interface, say IReadOnlyConnectionProperties, that will expose a readonly view of its members.

public interface IReadOnlyConnectionProperties 
{
    string Name { get; } 
    bool Enabled { get; } 
} 

If you don't control the type, you can make such an interface and create a wrapper type that will implement it. It would take ConnectionProperties in its ctor and proxy get-accesses to it properties. The same way a ReadOnlyCollection does.

internal ReadOnlyConnectionProperties : IReadOnlyConnectionProperties 
{
    private readonly ConnectionProperties _wrapped;

    public string Name => _wrapped.Name;
    public bool Enabled => _wrapped.Enabled;

    public ReadOnlyConnectionProperties(
        ConnectionProperties connectionProperties) =>
        _wrapped = connectionProperties;
} 

Note that this assumes a level of trust to the user. If using the first solution, there's nothing stopping the consumers from upcasting the interface back to ConnectionProperties. The second solution avoids this problem, and is the one I'd recommend. Just remember that in both cases changes to the underlying ConnectionProperties will be reflected in whatever proxies you give to your consumers.

Upvotes: 4

vasil oreshenski
vasil oreshenski

Reputation: 2836

You can create something like readonly interface for the ConnectionProperties class and expose it in the Data class by the readonly interface. This way the ConnectionProprties will have the same functionality.

 internal interface IConnectionProperties
 {
        string Name { get; }
        bool Enabled { get; }
 }

 internal class ConnectionProperties : IConnectionProperties
 {
        public string Name { get; set; } = "Default Name";
        public bool Enabled { get; set; } = false;
 }

internal class Data
{
    private ConnectionProperties _connection;
    internal IConnectionProperties Connection => this._connection;
}

The only change is that you need to make the ConnectionProperties's class properties public - Name, Enabled.

Upvotes: 1

Related Questions