我应该使用结构或类来表示纬度/经度坐标吗?

如何解决我应该使用结构或类来表示纬度/经度坐标吗?

| 我正在使用地理编码API,需要将返回点的坐标表示为纬度/经度对。但是,我不确定是否为此使用结构或类。我最初的想法是使用结构,但是在C#中它们似乎普遍不被接受(例如,乔恩·斯凯特(Jon Skeet)在此答案中提到“我几乎从不定义自定义结构”)。性能和内存使用不是应用程序中的关键因素。 到目前为止,我已经基于一个简单的界面提出了这两个实现: 接口
public interface ILatLng
{
    double Lat { get; }
    double Lng { get; }
}
LatLng类的实现
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结构实现
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},this.Lng);
    }
}
执行一些测试,我得出以下发现: 结构始终具有无参数的构造函数,这意味着您不能像使用类那样强制使用需要两个属性(对于lat和lng)的构造函数来实例化该结构。 结构(作为值类型)永远不能为null,因此将始终包含值。但是,如果实现接口,您仍然可以执行以下操作:
ILatLng s = new SLatLng(); s = null;
那么,在这种情况下,结构使用接口有意义吗? 如果使用结构,是否需要覆盖
Equals
GetHashCode()
等?我的测试表明,比较无需进行比较即可正常工作(与类不同)-因此有必要吗? 使用类让我感觉更“舒服”,所以当我更加了解它们的行为时,最好还是坚持使用它们?使用我的代码的人会被值类型的语义所迷惑吗,尤其是在使用接口时? 在
CLatLng
实现中,覆盖
GetHashCode()
似乎可以吗?我从本文中“偷了”它,所以不确定! 感激收到任何帮助或建议!     

解决方法

        老实说,我看不出为此有一个接口。 我只是创建一个结构,但使其不可变-可变结构是一个非常糟糕的主意。我还要使用完整的
Latitude
Longitude
作为属性名称。像这样:
public struct GeoCoordinate
{
    private readonly double latitude;
    private readonly double longitude;

    public double Latitude { get { return latitude; } }
    public double Longitude { get { return longitude; } }

    public GeoCoordinate(double latitude,double longitude)
    {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public override string ToString()
    {
        return string.Format(\"{0},{1}\",Latitude,Longitude);
    }
}
然后我还要实现
IEquatable<GeoCoordinate>
并覆盖
Equals
GetHashCode
,例如
public override bool Equals(Object other)
{
    return other is GeoCoordinate && Equals((GeoCoordinate) other);
}

public bool Equals(GeoCoordinate other)
{
    return Latitude == other.Latitude && Longitude == other.Longitude;
}

public override int GetHashCode()
{
    return Latitude.GetHashCode() ^ Longitude.GetHashCode();
}
请注意,您需要了解对double进行相等比较的正常危险-这里没有太多选择,但是看起来好像应该相等的两个值可能不会... 关于无参数构造函数的观点是合理的,但是我怀疑您会发现它实际上并不会咬您。     ,        使其成为一个结构,以提高性能。 当您处理例如这些结构的数组。请注意,例如System.Collections.Generic.List可以正确处理.Net数组中元素类型的拆箱存储,因此它也适用于通用容器。 请注意,C#3.5+初始化器语法完全否定了您没有构造函数的事实:
new SLatLng { Lat = 1.0,Lng = 2.0 }
接口使用成本 请注意,添加接口不可避免地会降低性能:接口无法定义字段,没有字段的结构几乎没有用处。这只剩下一种现实的情况:该界面要求您定义访问字段的属性。 如果必须使用属性(通过getter / setter),则将失去直接访问的性能。比较: 带界面
public class X
{
    interface ITest { int x {get; } }
    struct Test : ITest
    {
        public int x { get; set; }
    }

    public static void Main(string[] ss)
    {
        var t = new Test { x=42 };
        ITest itf = t;
    }
}
生成设置器调用和装箱
.method public static  hidebysig 
       default void Main (string[] ss)  cil managed 
{
    // Method begins at RVA 0x20f4
.entrypoint
// Code size 29 (0x1d)
.maxstack 4
.locals init (
    valuetype X/Test    V_0,class X/ITest   V_1,valuetype X/Test    V_2)
IL_0000:  ldloca.s 0
IL_0002:  initobj X/Test
IL_0008:  ldloc.0 
IL_0009:  stloc.2 
IL_000a:  ldloca.s 2
IL_000c:  ldc.i4.s 0x2a
IL_000e:  call instance void valuetype X/Test::set_x(int32)
IL_0013:  ldloc.2 
IL_0014:  stloc.0 
IL_0015:  ldloc.0 
IL_0016:  box X/Test
IL_001b:  stloc.1 
IL_001c:  ret 
} // end of method X::Main
没有界面
public class Y
{
    struct Test
    {
        public int x;
    }

    public static void Main(string[] ss)
    {
        var t = new Test { x=42 };
        Test copy = t;
    }
}
产生直接分配和(显然)没有装箱
// method line 2
.method public static  hidebysig 
       default void Main (string[] ss)  cil managed 
{
    // Method begins at RVA 0x20f4
.entrypoint
// Code size 24 (0x18)
.maxstack 2
.locals init (
    valuetype Y/Test    V_0,valuetype Y/Test    V_1,valuetype Y/Test    V_2)
IL_0000:  ldloca.s 0
IL_0002:  initobj Y/Test
IL_0008:  ldloc.0 
IL_0009:  stloc.2 
IL_000a:  ldloca.s 2
IL_000c:  ldc.i4.s 0x2a
IL_000e:  stfld int32 Y/Test::x
IL_0013:  ldloc.2 
IL_0014:  stloc.0 
IL_0015:  ldloc.0 
IL_0016:  stloc.1 
IL_0017:  ret 
} // end of method Y::Main
    ,结构和值类型是.net对象层次结构之外的实体,但是每次定义结构时,系统还会定义一个从ValueType派生的伪类,该类在很大程度上类似于该结构。扩展转换运算符在struct和伪类之间定义。请注意,声明为接口类型的变量,参数和字段始终作为类对象进行处理。如果某种程度上将某种东西用作接口,那么在许多情况下,它也可能是一类。 尽管有人抱怨可变结构的弊端,但在很多地方,具有可变语义的可变结构将很有用,如果不是因为系统在处理它们时的不足之处。例如: 更改“自我”的方法和属性应使用禁止在只读上下文中应用的属性进行标记。除非使用兼容性开关进行编译,否则应禁止不具有此类属性的方法更改\“ self \\”。 应该有一种方法,可以通过将某些表达式内向外或者通过使用标准类型的property-delegate-pair来传递对结构或其字段的引用,以方便诸如myDictOfPoints(\“ George \”)。X ++之类的事情。 通常,值类型语义学比参考语义学要“期望的”更多,这不仅是因为前者在某些通用语言中的支持如此差。 PS:我建议,尽管可变结构通常是一件好事和适当的事情,但变异“自我”的结构成员处理不当,应避免使用。使用将返回新结构的函数(例如\“ AfterMoving(Double distanceMiles,Double headingDegrees)\”),该函数将返回一个新的LatLong,其位置将是在移动指定距离后的位置),或者使用静态方法(例如\“ MoveDistance(参考纬度长位置,Double distanceMiles,Double heading Degrees \”)。可变结构通常应在基本上代表一组变量的地方使用。     ,        我会用一个结构。在这里,类是简单易用的方法,您可以看一下其他结构,例如Point。     ,        您正在尝试制作一个可变的结构,这是不行的。特别是因为它实现了一个接口! 如果您想使
LatLng
型可变,请坚持使用参考型。否则,struct对于此特定示例而言是合适的。     ,        您的情况下不需要接口。只是把它做成普通的
struct
。通过接口将when21ѭ通过时,这将防止任何不必要的装箱。     ,        我真的很喜欢此坐标库在Codeplex上提供的证明和指导。在其中,他们使用类并表示lat的实际值,而使用float则表示它们的长值。     ,        就个人而言,即使它们是像LatLong这样的简单类,我也喜欢使用它们。自C ++以来,我从未使用过结构。类的另一个优点是,如果需要更复杂的功能,将来可以扩展它们。 我确实同意该线程上的其他观点,尽管我不完全了解您正在使用的对象的上下文,但是在您的应用程序中可能需要一个接口,这似乎是一个过大的杀伤力。 最后,您的GetHashCode似乎是“ Lat * Long \”的美化方法。我不确定这是否安全。另外,如果您打算在应用程序中多次使用GetHashCode,我建议保持简化以提高该方法的性能。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-