这有效:
EndPoint endPoint = new IPEndPoint(_address, _port);
_socket.ReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref endPoint);
但这不是:
IPEndPoint endPoint = new IPEndPoint(_address, _port);
_socket.ReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref endPoint);
(注意endPoint的类型)
这看起来很奇怪。为什么ref关键字会破坏参数的逆转?
答案 0 :(得分:17)
因为在方法签名中,endPoint
参数声明为EndPoint
,而不是IPEndPoint
;无法保证该方法不会将endPoint
设置为另一种EndPoint
,而IPEndPoint
不能分配给FooEndPoint
变量。
例如,假设您有一个继承自EndPoint
的{{1}}类,以及一个带有Foo
参数的ref EndPoint
方法:
public class FooEndPoint : EndPoint
{
...
}
public void Foo(ref EndPoint endPoint)
{
...
endPoint = new FooEndPoint();
...
}
如果您能够将IPEndPoint
传递给该方法,FooEndPoint
对endPoint
参数的分配将在运行时失败,因为FooEndPoint
不是一个IPEndPoint
答案 1 :(得分:2)
因为ReceiveFrom方法可以创建新的EndPoint,但不能创建IPEndPoint。此参数有两种方式,因此类型需要完全匹配。
答案 2 :(得分:0)
ref
和out
参数的矛盾之处在于它们是引用。这意味着,当您将普通参数传递给方法时,CLR会解析对该值的引用,并将该值作为参数传递,而当参数为ref
或out
时,CLR会传递参数整个引用作为参数。 .NET中的引用类型为WeakReference<T>
,并且您将引用包装在其中。
赋予WeakReference<T>
的通用参数是您引用的类型,例如:
public void Test<T>(ref T reference)
{
WeakReference<T> weakReference = __makeref(reference);
}
现在,我将进一步介绍另一个示例:
public void Test(ref string reference)
{
WeakReference<string> weakReference = __makeref(reference);
}
这是另外一个:
public void Test(ref string reference)
{
WeakReference<object> weakReference = __makeref(reference);
}
后面的示例将给您一个编译器错误:如您所见,泛型参数不是协变的。因此,当您将参数的引用包装到WeakReference<T>
时,T
必须恰好是该参数的类型。
即使您没有使用__makeref
,您也可以理解CLR的内部行为就是这样。
此外,在较低级别中,引用是包含值的类型和值本身的结构。因此,如果您可以将引用转换为其任何超类型,则将更改引用的类型,当您尝试获取其值时,它会失败,因为值的类型因引用类型的不同而不同内。