Skip to content

Commit 093727c

Browse files
authored
Added == and != operator overloads to IpAddress (#310)
1 parent bbf11f4 commit 093727c

File tree

5 files changed

+108
-30
lines changed

5 files changed

+108
-30
lines changed

Tests/IPAddressTests/IPAddressTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace NFUnitTestIPAddress
1616
public class IPAddressTests
1717
{
1818
[Setup]
19-
public void SetupConnectToEthernetTests()
19+
public void Setup()
2020
{
2121
// Comment next line to run the tests on a real hardware
2222
Assert.SkipTest("Skipping tests using nanoCLR Win32 in a pipeline");
@@ -301,5 +301,24 @@ public void NetTest6_SocketAddressBasic()
301301
+ " as " + socketAddress2.ToString() + " " + socketAddress2.GetHashCode());
302302
}
303303
}
304+
305+
[TestMethod]
306+
public void Equality_Tests()
307+
{
308+
var privateAddress = IPAddress.Parse("192.168.0.1");
309+
var publicAddress = IPAddress.Parse("1.1.1.1");
310+
311+
// Equal
312+
Assert.AreEqual(privateAddress, IPAddress.Parse("192.168.0.1"));
313+
Assert.AreEqual(publicAddress, IPAddress.Parse("1.1.1.1"));
314+
Assert.IsTrue(privateAddress == new IPAddress(new byte[] { 192, 168, 0, 1 }), "192.168.0.1 == 192.168.0.1");
315+
Assert.IsTrue(publicAddress == new IPAddress(new byte[] { 1, 1, 1, 1 }), "1.1.1.1 == 1.1.1.1");
316+
317+
// Not Equal
318+
Assert.AreNotEqual(privateAddress, IPAddress.Parse("1.1.1.1"));
319+
Assert.AreNotEqual(publicAddress, IPAddress.Parse("192.168.0.1"));
320+
Assert.IsTrue(privateAddress != new IPAddress(new byte[] { 192, 168, 0, 2 }), "192.168.0.1 == 192.168.0.2");
321+
Assert.IsTrue(publicAddress != new IPAddress(new byte[] { 1, 1, 1, 2 }), "1.1.1.1 == 1.1.1.2");
322+
}
304323
}
305324
}

Tests/IPAddressTests/IPAddressTests.nfproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
<ItemGroup>
6262
<ProjectReference Include="..\..\nanoFramework.System.Net\System.Net.nfproj" />
6363
</ItemGroup>
64+
<ItemGroup>
65+
<Content Include="packages.lock.json" />
66+
</ItemGroup>
6467
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
6568
<!-- MANUAL UPDATE HERE -->
6669
<ProjectExtensions>

Tests/IPAddressTests/nano.runsettings

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
<RunSettings>
33
<!-- Configurations that affect the Test Framework -->
44
<RunConfiguration>
5-
<MaxCpuCount>1</MaxCpuCount>
65
<ResultsDirectory>.\TestResults</ResultsDirectory><!-- Path relative to solution directory -->
76
<TestSessionTimeout>120000</TestSessionTimeout><!-- Milliseconds -->
87
<TargetFrameworkVersion>net48</TargetFrameworkVersion>
98
<TargetPlatform>x64</TargetPlatform>
109
</RunConfiguration>
1110
<nanoFrameworkAdapter>
12-
<Logging>None</Logging>
13-
<IsRealHardware>False</IsRealHardware>
11+
<Logging>None</Logging> <!--Set to the desired level of logging for Unit Test execution. Possible values are: None, Detailed, Verbose, Error. -->
12+
<IsRealHardware>False</IsRealHardware><!--Set to true to run tests on real hardware. -->
13+
<RealHardwarePort>COM3</RealHardwarePort><!--Specify the COM port to use to connect to a nanoDevice. If none is specified, a device detection is performed and the 1st available one will be used. -->
14+
<CLRVersion></CLRVersion><!--Specify the nanoCLR version to use. If not specified, the latest available will be used. -->
15+
<PathToLocalCLRInstance></PathToLocalCLRInstance><!--Specify the path to a local nanoCLR instance. If not specified, the default one installed with nanoclr CLR witll be used. -->
1416
</nanoFrameworkAdapter>
1517
</RunSettings>

nanoFramework.System.Net/IPAddress.cs

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,38 @@ public class IPAddress
2424
/// <summary>
2525
/// Provides an IP address that indicates that the server must listen for client activity on all network interfaces. This field is read-only.
2626
/// </summary>
27-
public static readonly IPAddress Any = new(0x0000000000000000);
27+
/// <remarks>
28+
/// The <see cref="Socket.Bind"/> method uses the <see cref="Any"/> field to indicate that a <see cref="Socket"/> instance must listen for client activity on all network interfaces.
29+
///
30+
/// The <see cref="Any"/> field is equivalent to 0.0.0.0 in dotted-quad notation.
31+
/// </remarks>
32+
public static readonly IPAddress Any = new(new byte[] { 0, 0, 0, 0 });
2833

2934
/// <summary>
3035
/// Provides the IP loopback address. This field is read-only.
3136
/// </summary>
32-
public static readonly IPAddress Loopback = new(0x000000000100007F);
37+
/// <remarks>
38+
/// The <see cref="Loopback"/> field is equivalent to 127.0.0.1 in dotted-quad notation.
39+
/// </remarks>
40+
public static readonly IPAddress Loopback = new(new byte[] { 127, 0, 0, 1 });
41+
42+
/// <summary>
43+
/// Provides the IP broadcast address. This field is read-only.
44+
/// </summary>
45+
/// <remarks>
46+
/// The <see cref="Broadcast"/> field is equivalent to 255.255.255.255 in dotted-quad notation.
47+
/// </remarks>
48+
public static readonly IPAddress Broadcast = new(new byte[] { 255, 255, 255, 255 });
49+
50+
/// <summary>
51+
/// Provides an IP address that indicates that no network interface should be used. This field is read-only.
52+
/// </summary>
53+
/// <remarks>
54+
/// The <see cref="Socket.Bind"/> uses the <see cref="None"/> field to indicate that a <see cref="Socket"/> must not listen for client activity.
55+
///
56+
/// The <see cref="None"/> field is equivalent to 255.255.255.255 in dotted-quad notation.
57+
/// </remarks>
58+
public static readonly IPAddress None = Broadcast;
3359

3460
internal readonly long Address;
3561

@@ -136,6 +162,22 @@ public IPAddress(byte[] address)
136162
}
137163
}
138164

165+
/// <summary>
166+
/// Indicates whether two <see cref="IPAddress"/> objects are equal.
167+
/// </summary>
168+
/// <param name="a">The <see cref="IPAddress"/> to compare with <paramref name="b"/>.</param>
169+
/// <param name="b">The <see cref="IPAddress"/> to compare with <paramref name="a"/>.</param>
170+
/// <returns><see langword="true"/> if <paramref name="b"/> is equal to <paramref name="a"/>; otherwise, <see langword="false"/>.</returns>
171+
public static bool operator ==(IPAddress a, IPAddress b) => a is not null && a.Equals(b);
172+
173+
/// <summary>
174+
/// Indicates whether two <see cref="IPAddress"/> objects are not equal.
175+
/// </summary>
176+
/// <param name="a">The <see cref="IPAddress"/> to compare with <paramref name="b"/>.</param>
177+
/// <param name="b">The <see cref="IPAddress"/> to compare with <paramref name="a"/>.</param>
178+
/// <returns><see langword="true"/> if <paramref name="b"/> is not equal to <paramref name="a"/>; otherwise, <see langword="false"/>.</returns>
179+
public static bool operator !=(IPAddress a, IPAddress b) => !(a == b);
180+
139181
/// <summary>
140182
/// Initializes a new instance of a IPV6 <see cref="IPAddress"/> class with the address specified as a Byte array.
141183
/// </summary>
@@ -173,45 +215,59 @@ private IPAddress(ushort[] address, uint scopeid)
173215
/// <summary>
174216
/// Compares two IP addresses.
175217
/// </summary>
176-
/// <param name="obj">An <see cref="IPAddress"/> instance to compare to the current instance.</param>
177-
/// <returns></returns>
178-
public override bool Equals(object obj)
218+
/// <param name="other">An <see cref="IPAddress"/> instance to compare to the current instance.</param>
219+
/// <returns><see langword="true"/> if the two addresses are equal; otherwise, <see langword="false"/>.</returns>
220+
public override bool Equals(object other)
179221
{
180-
IPAddress addr = obj as IPAddress;
222+
return other is IPAddress ipAddress && Equals(ipAddress);
223+
}
181224

182-
if (obj == null) return false;
225+
/// <summary>
226+
/// Compares two IP addresses.
227+
/// </summary>
228+
/// <param name="other">An <see cref="IPAddress"/> instance to compare to the current instance.</param>
229+
/// <returns><see langword="true"/> if the two addresses are equal; otherwise, <see langword="false"/>.</returns>
230+
public bool Equals(IPAddress other)
231+
{
232+
if (other is null)
233+
{
234+
return false;
235+
}
236+
237+
// Compare families before address representations
238+
if (AddressFamily != other.AddressFamily)
239+
{
240+
return false;
241+
}
183242

184243
// Compare family before address
185-
if (_family != addr.AddressFamily)
244+
if (_family != other.AddressFamily)
186245
{
187246
return false;
188247
}
189248

190249
if (_family == AddressFamily.InterNetworkV6)
191250
{
192251
// For IPv6 addresses, compare the full 128bit address
193-
for (int i = 0; i < NumberOfLabels; i++)
252+
for (var i = 0; i < NumberOfLabels; i++)
194253
{
195-
if (addr._numbers[i] != this._numbers[i])
254+
if (other._numbers[i] != _numbers[i])
196255
return false;
197256
}
198257

199258
// Also scope must match
200-
if (addr._scopeid == this._scopeid)
201-
return true;
202-
203-
return false;
259+
return other._scopeid == _scopeid;
204260
}
205261
else
206262
{
207-
return this.Address == addr.Address;
263+
return Address == other.Address;
208264
}
209265
}
210266

211267
/// <summary>
212-
/// Provides a copy of the <see cref="IPAddress"/> as an array of bytes.
268+
/// Provides a copy of the <see cref="IPAddress"/> as an array of bytes in network order.
213269
/// </summary>
214-
/// <returns>A Byte array.</returns>
270+
/// <returns>A <see langword="byte"/> array.</returns>
215271
public byte[] GetAddressBytes()
216272
{
217273
byte[] bytes;
@@ -223,8 +279,8 @@ public byte[] GetAddressBytes()
223279
int j = 0;
224280
for (int i = 0; i < NumberOfLabels; i++)
225281
{
226-
bytes[j++] = (byte)((this._numbers[i] >> 8) & 0xFF);
227-
bytes[j++] = (byte)((this._numbers[i]) & 0xFF);
282+
bytes[j++] = (byte)((_numbers[i] >> 8) & 0xFF);
283+
bytes[j++] = (byte)((_numbers[i]) & 0xFF);
228284
}
229285
return bytes;
230286
}
@@ -341,11 +397,9 @@ public override int GetHashCode()
341397
{
342398
return ToString().GetHashCode();
343399
}
344-
else
345-
{
346-
// For IPv4 addresses, we can simply use the integer representation.
347-
return unchecked((int)Address);
348-
}
400+
401+
// For IPv4 addresses, we can simply use the integer representation.
402+
return unchecked((int)Address);
349403
}
350404

351405
// For security, we need to be able to take an IPAddress and make a copy that's immutable and not derived.
@@ -410,7 +464,7 @@ public IPAddress MapToIPv6()
410464
return this;
411465
}
412466

413-
ushort[] labels = new ushort[IPAddress.NumberOfLabels];
467+
ushort[] labels = new ushort[NumberOfLabels];
414468
labels[5] = 0xFFFF;
415469
labels[6] = (ushort)(((Address & 0x0000FF00) >> 8) | ((Address & 0x000000FF) << 8));
416470
labels[7] = (ushort)(((Address & 0xFF000000) >> 24) | ((Address & 0x00FF0000) >> 8));

nanoFramework.System.Net/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
////////////////////////////////////////////////////////////////
1313
// update this whenever the native assembly signature changes //
14-
[assembly: AssemblyNativeVersion("100.2.0.0")]
14+
[assembly: AssemblyNativeVersion("100.2.0.1")]
1515
////////////////////////////////////////////////////////////////
1616

1717
// Setting ComVisible to false makes the types in this assembly not visible

0 commit comments

Comments
 (0)