Question

This works:

EndPoint endPoint = new IPEndPoint(_address, _port);
_socket.ReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref endPoint);

But this does not:

IPEndPoint endPoint = new IPEndPoint(_address, _port);
_socket.ReceiveFrom(buffer, 0, 1024, SocketFlags.None, ref endPoint);

(Note the type of endPoint)

Which seems odd. Why does the ref keyword break parameter contravariance?

Was it helpful?

Solution

Because in the method signature, the endPoint parameter is declared as EndPoint, not IPEndPoint ; there is no guarantee that the method won't set endPoint to another kind of EndPoint, which would not be assignable to a IPEndPoint variable.

For instance, assume you have a FooEndPoint class that inherits from EndPoint, and a Foo method that takes a ref EndPoint parameter :

public class FooEndPoint : EndPoint
{
   ...
}

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

If you were able to pass a IPEndPoint to that method, the assigment of a FooEndPoint to the endPoint parameter would fail at runtime, because a FooEndPoint is not a IPEndPoint

OTHER TIPS

Because the method ReceiveFrom can create a new EndPoint - but not IPEndPoint. This parameter works kind of in two ways, so the type needs to match exactly.

The contraviariance is lose on ref and out parameters because they are references. That means when you pass a normal parameter to a method, CLR will solve that reference to a value and will pass that value as an argument, whereas when a parameter is either ref or out CLR will pass the whole reference as an argument. The reference type in .NET is WeakReference<T> and you're wrapping your reference in that.
The generic argument given to WeakReference<T> is the type your reference is, for example:

public void Test<T>(ref T reference)
{
    WeakReference<T> weakReference = __makeref(reference);
}

Now, I'm going deeper with another example:

public void Test(ref string reference)
{
    WeakReference<string> weakReference = __makeref(reference);
}

And this is one more:

public void Test(ref string reference)
{
    WeakReference<object> weakReference = __makeref(reference);
}

The latter example will give you a compiler error: as you can see, generic parameters are not contravariant. Therefore, when you wrap the reference of the parameter to a WeakReference<T>, T must be exactly the type of that parameter.

Even if you're not using __makeref, you could understand that the internal behavior of CLR is such a thing.
Furthermore, in a lower level references are structures containing the type of the value and the value itself. So, if you could cast a reference to any of its super types, you'll change the type of the reference and when you try to get its value, it fails because the type of the value is different by the type of the reference it's in.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top