Почему параметры ссылок не являются контрвариантными? - PullRequest
4 голосов
/ 28 августа 2009

Это работает:

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 нарушает контрастность параметров?

Ответы [ 3 ]

17 голосов
/ 28 августа 2009

Поскольку в сигнатуре метода параметр endPoint объявлен как EndPoint, а не IPEndPoint; нет никакой гарантии, что метод не установит endPoint в другой тип EndPoint, который не может быть назначен переменной IPEndPoint.

Например, предположим, что у вас есть класс FooEndPoint, который наследуется от EndPoint, и метод Foo, который принимает параметр ref EndPoint:

public class FooEndPoint : EndPoint
{
   ...
}

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

Если бы вы смогли передать IPEndPoint этому методу, присвоение FooEndPoint параметру endPoint не выполнится во время выполнения, поскольку FooEndPoint не является IPEndPoint

2 голосов
/ 28 августа 2009

Поскольку метод ReceiveFrom может создать новую EndPoint, но не IPEndPoint. Этот параметр работает в двух вариантах, поэтому тип должен точно соответствовать.

0 голосов
/ 14 ноября 2018

Противопоказание потеряно для параметров 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 - такая вещь.
Кроме того, ссылки более низкого уровня представляют собой структуры, содержащие тип значения и само значение. Таким образом, если вы можете привести ссылку на любой из ее супертипов, вы измените тип ссылки, и когда вы попытаетесь получить ее значение, произойдет сбой, потому что тип значения отличается в зависимости от типа ссылки, к которой он относится. в.

...