4

这有效:

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);

(注意端点的类型)

这似乎很奇怪。为什么 ref 关键字会破坏参数的逆变性?

4

3 回答 3

17

因为在方法签名中,endPoint参数被声明为EndPoint,而不是IPEndPoint; 不能保证该方法不会设置endPoint为另一种EndPoint不能分配给IPEndPoint变量的 。

例如,假设您有一个FooEndPoint继承自 的类EndPoint,以及一个Foo采用ref EndPoint参数的方法:

public class FooEndPoint : EndPoint
{
   ...
}

public void Foo(ref EndPoint endPoint)
{
    ...
    endPoint = new FooEndPoint();
    ...
}

如果您能够将 a 传递IPEndPoint给该方法,则将 aFooEndPoint分配给endPoint参数将在运行时失败,因为 aFooEndPoint不是IPEndPoint

于 2009-08-28T12:47:29.240 回答
2

因为 ReceiveFrom 方法可以创建新的 EndPoint - 但不能创建 IPEndPoint。这个参数有两种工作方式,所以类型需要完全匹配。

于 2009-08-28T12:49:08.930 回答
0

因为它们是引用,所以逆变ref和参数丢失。out这意味着当您将普通参数传递给方法时,CLR 将解决对该值的引用并将该值作为参数传递,而当参数是ref或时, outCLR 会将整个引用作为参数传递。.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);
}

后一个示例会给您一个编译器错误:如您所见,泛型参数不是逆变的。因此,当您将参数的引用包装到 aWeakReference<T>时,T必须正是该参数的类型。

即使您不使用__makeref,您也可以理解 CLR 的内部行为是这样的。
此外,在较低级别中,引用是包含值类型和值本身的结构。因此,如果您可以将引用转换为它的任何超类型,您将更改引用的类型,当您尝试获取它的值时,它会失败,因为值的类型因引用的类型而异在。

于 2018-11-14T09:56:11.617 回答