Antony Scott
Antony Scott

Reputation: 21998

Privately scoped variable only for use within property

Is it possible to have a variable which is an instance variable within a class but can only be accessed by a specific property?

I quite often create "self-creating" properties like so ...

private IWGSLocation _location;
public IWGSLocation Location
{
    get
    {
        _location = _location ?? new WGSLocation();
        _location.Latitude = Latitude.GetValueOrDefault(0);
        _location.Longitude = Longitude.GetValueOrDefault(0);
        return _location;
    }
}

which means that I don't go re-creating a new WGSLocation (or whatever other kind of object I need, which may be expensive to create, or may only need to be created once) every time I access the property. The downside is that my class can access the _location variable. But I don't really want it to, so if there any way of having an instance variable which can only be used within the property itself?

I'm thinking something along these lines ...

public IWGSLocation Location
{
    get
    {
        WGSLocation _location = _location ?? new WGSLocation();
        _location.Latitude = Latitude.GetValueOrDefault(0);
        _location.Longitude = Longitude.GetValueOrDefault(0);
        return _location;
    }
}

Upvotes: 3

Views: 229

Answers (3)

CodesInChaos
CodesInChaos

Reputation: 108810

Your current implementation looks broken to me.

var x=obj.Location;
x.Latitude = 1;
Console.WriteLine(x.Latitude);//1
var y=obj.Location;
Console.WriteLine(x.Latitude);//WTF it changed

I recommend making IWGSLocation immutable, or only modifying it on creation, depending on which semantics you want.

Upvotes: 1

Eric Lippert
Eric Lippert

Reputation: 660138

I agree it would be a nice language feature to have persistent locals -- that is, variables whose lifetimes are based on the lifetime of the instance but whose scopes (the region of program text in which it is legal to access the variable by name) are local. It would be nice to have "static" locals as well, as some languages do.

Sadly, this is not a feature of C# and we have no plans to add it. It's nice to have, but nice to have is not good enough to justify the expense, or to postpone or cancel a "nicer to have" feature.

It's only "nice to have" because of course if you have a private field, it already is a private implementation detail of the class. If you don't want it used outside the property, then don't write code that uses it outside the property. If one of your coworkers tries to do so, put the smack down on 'em in code review.

I thought I might add: be very careful when writing property getters that mutate state. By default property getters are evaluated while looking at an object in the debugger, and it can be very confusing to be debugging something and have the debugger changing the values of fields just because you are examining an object.

Upvotes: 5

Adam Houldsworth
Adam Houldsworth

Reputation: 64487

The fact that the class can access it isn't necessarily a downside. It is still logically encapsulated in the same entity.

What you are after isn't possible in the way you want it to be. Member variables are visible by all areas of the class and local variables are restricted to their defined scope.

What you could instead do is have the location wrapped inside a container class. This class is your member variable. When returning the IWGSLocation you simply drill into the container class:

public class LocationContainer
{
    public IWGSLocation InnerLocation { get; private set; }

    public void SetLocation(WGSLocation loc)
    {
        InnerLocation = loc;
    }
}

private readonly LocationContainer _container = new LocationContainer();

public IWGSLocation Location
{
    get
    {
        if (_container.InnerLocation == null)
        {
            _container.SetLocation(...);
        } 

        return _container.InnerLocation;
    }
}

This won't stop the class from touching _container, but it will make other developers think twice before they do and they won't be able to accidentally mutate the location without explicitly calling SetLocation.

You could even then put set-once guarding in the SetLocation of the container.

Update: I'd actually use the lazy class here, something like:

private readonly Lazy<IWGSLocation> _location = new Lazy<IWGSLocation>(()
 =>
{
    var l = new WGSLocation();
    l.Latitude = Latitude.GetValueOrDefault(0);
    l.Longitude = Longitude.GetValueOrDefault(0);
    return l;
});

public IWGSLocation Location
{
    get { return _location.Value; }
}

Be warned, this was head-compiled! :-)

Upvotes: 1

Related Questions