为什么IPAddress有时会抛出ArgumentOutOfRangeException?

9

我只是在尝试调试以下异常:

Exception has been thrown by the target of an invocation.: System.Reflection.TargetInvocationException: 
Exception has been thrown by the target of an invocation. ---> 
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: newAddress
   at System.Net.IPAddress..ctor(Int64 newAddress)

这是相关代码:

int hostToNetworkOrder = IPAddress.HostToNetworkOrder( (int)computer.IpAddress );
IPAddress ipAddress = new IPAddress( hostToNetworkOrder );

computer.IpAddress被设置为1582281193。这将被转换为hostToNetworkOrder,即-374255778

现在,如果我尝试在Visual Studio的即时窗口中构造new IPAddress(-374255778);,我会得到一个包含正确IP地址的IPAddress对象。

但是,如果我越过代码行,它将总是抛出ArgumentOutOfRangeException异常。为什么?


更新

我知道我的输入是负数很可能就是问题的源头。这就是我解决问题的方法:

UInt32 hostToNetworkOrder = (UInt32)IPAddress.HostToNetworkOrder( (Int32)computer.IpAddress );
IPAddress ipAddress = new IPAddress( hostToNetworkOrder );

我还不明白的是为什么在即时窗口中它能够工作。
4个回答

8

在ILSpy中查看IPAddress类,有一个内部构造函数,它接受一个int而不进行范围检查——我有一种感觉,在监视窗口中可能会调用该构造函数,但在实际代码中,它调用公共构造函数(long),该函数验证该数字。

为了测试我的理论,我只是尝试了这段代码:

var ipAddress = (IPAddress) typeof (IPAddress)
                                        .GetConstructor(
                                            BindingFlags.NonPublic | BindingFlags.Instance,
                                            null,
                                            new[] {typeof (int)},
                                            null)
                                        .Invoke(new object[] {hostToNetworkOrder });

而且,IPAddress类确实没有出现错误。


+1 不错的调查!我之前一直在使用 JustDecompile 做同样的事情 - Int32 构造函数是 internal 的,而 Int64 构造函数是 public 的。 - kaj

3

我仍然不明白的是为什么它在即时窗口中起作用。

这是你问题的关键。你没有考虑到调试器表达式求值器的一些奇怪行为,它用于解析即时窗口中的表达式。最好通过一个类似IPAddress的代码示例来演示这一点。创建一个控制台模式项目并添加一个类库项目。让类库源代码文件看起来像这样:

using System;

namespace Foo {
    public class Test {
        public string how;
        internal Test(int arg) { how = "internal"; }
        public Test(long arg) { how = "public"; }
        public string ToString() { return how; }
    }
}

控制台应用程序的 Program.cs 文件如下:

using System;
using Foo;

class Program {
    static void Main(string[] args) {
        new Test(999);
    }   // <== set breakpoint here
}

按下 F5 并在断点触发时切换到立即窗口。输入以下内容:
new Foo.Test(-1).ToString()

您将会看到:

"internal"

它选择了内部构造函数而不是公共构造函数。IPAddress也发生了同样的事情,它有一个接受int类型参数的内部构造函数,该构造函数不检查参数。虽然这听起来像是一个错误,但提供对私有和内部成员的访问是调试器的正常行为。总的来说,请记住调试器表达式求值器并非由C#编译器驱动。它不使用与语言完全相同的规则,也没有与语言一样的完整表达能力。希望在Roslyn项目完成后,这种情况会有所改善。

1

当IP地址小于零或大于0x00000000FFFFFFFF时,会出现System.ArgumentOutOfRangeException,请参见此处

IPAddress构造函数需要一个long类型的参数(64位整数),但您正在传递一个32位整数,它会被转换为long。


这是我假设的。但是为什么在即时窗口中构建对象时它可以工作呢? - Oliver Salzburg

0

我遇到了同样的问题,我正在将IP转换为int32:

    Int32 IP1 = Convert.ToInt32(arrIP[0]);
    Int32 IP2 = Convert.ToInt32(arrIP[1]);
    Int32 IP3 = Convert.ToInt32(arrIP[2]);
    Int32 IP4 = Convert.ToInt32(arrIP[3]);
    long ipInt = IP1 + (IP2 * 256) + (IP3 * 256 * 256) + (IP4 * 256 * 256 * 256);
    IPAddress ipadr = new IPAddress(ipInt);
    IPEndPoint ipe = new IPEndPoint(ipadr, port);

对于某些IP地址,它会转换为负数。

我所做的唯一更改是使用int64,这解决了我的问题。

        Int64 IP1 = Convert.ToInt64(arrIP[0]);
        Int64 IP2 = Convert.ToInt64(arrIP[1]);
        Int64 IP3 = Convert.ToInt64(arrIP[2]);
        Int64 IP4 = Convert.ToInt64(arrIP[3]);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接