Should I use a struct or a class to represent a Lat/Lng coordinate?

I am working a with a geo-coding API and need to represent the coordinate of a returned point as a Latitude / Longitude pair. However, I am unsure whether to use a struct or a class for this. My initial thought was to use a struct, but they seem to be generally frowned upon in C# (for instance, Jon Skeet mentions in this answer that, "I almost never define custom structs"). Performance and memory usage are not critical factors in the application.

So far I have come up with these two implementations based on a simple interface:

Interface

public interface ILatLng
{
    double Lat { get; }
    double Lng { get; }
}

LatLng Class Implementation

public class CLatLng : ILatLng
{
    public double Lat { get; private set; }
    public double Lng { get; private set; }

    public CLatLng(double lat, double lng)
    {
        this.Lat = lat;
        this.Lng = lng;
    }

    public override string ToString()
    {
        return String.Format("{0},{1}", this.Lat, this.Lng);
    }

    public override bool Equals(Object obj)
    {
        if (obj == null)
            return false;

        CLatLng latlng = obj as CLatLng;
        if ((Object)latlng == null)
            return false;

        return (this.Lat == latlng.Lat) && (this.Lng == latlng.Lng);
    }

    public bool Equals(CLatLng latlng)
    {
        if ((object)latlng == null)
            return false;

        return (this.Lat == latlng.Lat) && (this.Lng == latlng.Lng);
    }


    public override int GetHashCode()
    {
        return (int)Math.Sqrt(Math.Pow(this.Lat, 2) * Math.Pow(this.Lng, 2));
    }
}

LatLng Struct Implementation

public struct SLatLng : ILatLng
{
    private double _lat;
    private double _lng;

    public double Lat
    {
        get { return _lat; }
        set { _lat = value; }
    }

    public double Lng
    {
        get { return _lng; }
        set { _lng = value; }
    }

    public SLatLng(double lat, double lng)
    {
        this._lat = lat;
        this._lng = lng;
    }

    public override string ToString()
    {
        return String.Format("{0},{1}", this.Lat, this.Lng);
    }
}

Performing some tests I've come to the following findings:

  • A struct always has a parameterless constructor, which means you can't force it to be instantiated with a constructor which expects two properties (for lat and lng), as you can with a class.

  • A struct (being a value type) can never be null, so will always contain a value. But you can still do stuff like this if implementing an interface:

    ILatLng s = new SLatLng(); s = null;

So does it make sense for a struct to use an interface in this case?

  • If I use a struct do I need to override Equals, GetHashCode() etc. ? My tests indicate comparisons work correctly without doing so (unlike with a class) - so is it necessary?

  • I feel more 'comfortable' using classes, so is it best to just stick with them as I'm more aware of how they behave? Will people using my code be confused by value-type semantics, especially when working to an interface?

  • In the CLatLng implementation, does the override of GetHashCode() seem OK? I 'stole' it from this article, so am unsure!

Any help or advice gratefully received!

37
задан Community 23 May 2017 в 11:46
поделиться