Dan*_*plo 37 .net c# struct geocoding
我正在使用地理编码API,需要将返回点的坐标表示为纬度/经度对.但是,我不确定是否为此使用结构或类.我最初的想法是使用一个结构,但它们似乎在C#中通常不赞成(例如,Jon Skeet 在这个答案中提到"我几乎从不定义自定义结构").性能和内存使用不是应用程序中的关键因素.
到目前为止,我已经基于一个简单的接口提出了这两个实现:
接口
public interface ILatLng
{
double Lat { get; }
double Lng { get; }
}
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
LatLng Struct实现
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);
}
}
Run Code Online (Sandbox Code Playgroud)
进行一些测试我得出以下发现:
struct总是有一个无参数的构造函数,这意味着你不能强制它用一个构造函数来实例化,这个构造函数需要两个属性(对于lat和lng),就像你可以使用一个类一样.
struct(作为值类型)永远不能为null,因此将始终包含值.但是如果实现一个接口,你仍然可以做这样的事情:
ILatLng s = new SLatLng(); s = null;
那么在这种情况下结构使用接口是否有意义?
如果我使用结构,我需要覆盖Equals,GetHashCode()等等?我的测试表明比较工作没有这样做(与课程不同) - 所以有必要吗?
我觉得使用课程更"舒服",所以最好坚持使用它们,因为我更了解它们的行为方式?使用我的代码的人是否会被值类型语义所困惑,尤其是在使用接口时?
在CLatLng实现中,覆盖GetHashCode()看起来好吗?我从这篇文章中偷了它,所以我不确定!
任何帮助或建议感激不尽!
Jon*_*eet 56
老实说,我无法看到为此设置界面有任何意义.
我只是创建一个结构,但让它不可变 - 可变结构是一个非常糟糕的主意.我也使用full 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);
}
}
Run Code Online (Sandbox Code Playgroud)
那么我也实现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();
}
Run Code Online (Sandbox Code Playgroud)
请注意,您需要了解对双精度执行相等比较的正常危险 - 这里没有太多替代方案,但两个看起来应该相等的值可能不是......
关于无参数构造函数的观点是合理的,但我怀疑你会发现它实际上不会咬你.
为了性能,使它成为一个结构.
请注意,C#3.5+ intializer语法完全否定了您无法使用构造函数的事实:
new SLatLng { Lat = 1.0, Lng = 2.0 }
Run Code Online (Sandbox Code Playgroud)请注意,添加接口不可避免地会降低性能:接口无法定义字段,没有字段的结构几乎不会有用.这只留下一个现实场景:界面要求您定义访问字段的属性.
如果您有义务使用这些属性(通过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;
}
}
Run Code Online (Sandbox Code Playgroud)
生成setter调用和装箱
.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
Run Code Online (Sandbox Code Playgroud)
public class Y
{
struct Test
{
public int x;
}
public static void Main(string[] ss)
{
var t = new Test { x=42 };
Test copy = t;
}
}
Run Code Online (Sandbox Code Playgroud)
生成直接分配和(显然)没有拳击
// 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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20656 次 |
| 最近记录: |