касательно и параметры в C# и не может быть отмечен как вариант

Что означает оператор?

Отсюда

касательно и параметры в C# и не может быть отмечен как вариант.

1) Это означает, что следующее не может быть сделано.

public class SomeClass: IVariant
{
    public virtual R DoSomething( ref A args )
    {
        return null;
    }
}

2) Или это означает, что у меня не может быть следующего.

public delegate R Reader(A arg, string s);

public static void AssignReadFromPeonMethodToDelegate(ref Reader pReader)
{
    pReader = ReadFromPeon;
}

static object ReadFromPeon(Peon p, string propertyName)
    {
        return p.GetType().GetField(propertyName).GetValue(p);
    }

static Reader pReader;

static void Main(string[] args)
    {
        AssignReadFromPeonMethodToDelegate(ref pReader);
        bCanReadWrite = (bool)pReader(peon, "CanReadWrite");

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey();
    }

Я попробовал (2), и это работало.

13
задан skaffman 20 May 2010 в 17:40
поделиться

2 ответа

"out" означает, грубо говоря, "появляется только в позициях вывода".

"in" означает, грубо говоря, "появляется только в позициях ввода".

На самом деле все немного сложнее, но ключевые слова были выбраны потому, что в большинстве случаев именно так и происходит.

Рассмотрим метод интерфейса или метод, представленный делегатом:

delegate void Foo</*???*/ T>(ref T item);

Появляется ли T в позиции ввода? Да. Вызывающий может передать значение T через элемент; вызываемый Foo может прочитать его. Поэтому T не может быть помечено как "out".

Появляется ли T в позиции вывода? Да. Вызывающая сторона может записать новое значение в item, которое вызывающая сторона может затем прочитать. Поэтому T не может быть помечено "in".

Поэтому, если T появляется в формальном параметре "ref", T не может быть помечен ни как in, ни как out.

Давайте рассмотрим несколько реальных примеров того, как все идет не так. Предположим, что это было бы законно:

delegate void X<out T>(ref T item);
...
X<Dog> x1 = (ref Dog d)=>{ d.Bark(); }
X<Animal> x2 = x1; // covariant;
Animal a = new Cat();
x2(ref a);

Well dog my cats, we just made a cat bark. "Выход" не может быть законным.

А как насчет "in"?

delegate void X<in T>(ref T item);
...
X<Animal> x1 = (ref Animal a)=>{ a = new Cat(); }
X<Dog> x2 = x1; // contravariant;
Dog d = new Dog();
x2(ref d);

Мы только что поместили кошку в переменную, которая может содержать только собак. T тоже не может быть помечена "in".

А как насчет параметра "out"?

delegate void Foo</*???*/T>(out T item);

? Теперь T появляется только в позиции output. Можно ли сделать так, чтобы T был помечен как "out"?

К сожалению, нет. Параметр "out" фактически не отличается от "ref" за кулисами. Единственная разница между "out" и "ref" заключается в том, что компилятор запрещает чтение из параметра out до того, как он будет присвоен вызывающей стороной, и что компилятор требует присвоения до нормального возврата вызывающей стороны. Тот, кто написал реализацию этого интерфейса на языке .NET, отличном от C#, смог бы читать из элемента до его инициализации, и поэтому он мог бы использоваться как входной. Поэтому мы запрещаем помечать T как "out" в этом случае. Это прискорбно, но мы ничего не можем с этим поделать; мы должны подчиняться правилам безопасности типов CLR.

Более того, правило для параметров "out" заключается в том, что они не могут быть использованы для ввода до записи в них. Нет правила, что они не могут быть использованы для ввода после записи в них. Предположим, мы разрешили

delegate void X<out T>(out T item);
class C
{
    Animal a;
    void M()
    {
        X<Dog> x1 = (out Dog d) => 
        { 
             d = null; 
             N(); 
             if (d != null) 
               d.Bark(); 
        };
        x<Animal> x2 = x1; // Suppose this were legal covariance.
        x2(out this.a);
    }
    void N() 
    { 
        if (this.a == null) 
            this.a = new Cat(); 
    }
}

еще раз заставить кошку лаять. Мы не можем разрешить T быть "вне".

Использовать параметры out для ввода таким образом очень глупо, но законно.


UPDATE: C# 7 добавил in в качестве формального объявления параметров, что означает, что теперь и in, и out означают две вещи; это создаст некоторую путаницу. Позвольте мне прояснить это:

  • in, out и ref в объявлении формального параметра в списке параметров означает "этот параметр является псевдонимом переменной, предоставленной вызывающей стороной".
  • ref означает "вызывающая сторона может читать или записывать переменную с псевдонимом, и она должна быть известна как присвоенная до вызова".
  • out означает "вызывающая сторона должна записать псевдонимную переменную через псевдоним до того, как она нормально вернется". Это также означает, что вызывающая сторона не должна читать псевдопеременную через псевдоним до того, как запишет ее, потому что переменная может быть не точно назначена.
  • in означает "вызывающая сторона может читать псевдонимную переменную, но не записывать в нее через псевдоним". Цель in - решить редкую проблему производительности, когда большая структура должна быть передана "по значению", но делать это дорого. Как деталь реализации, параметры in обычно передаются через значение размером с указатель, что быстрее, чем копирование по значению, но медленнее при разыменовании.
  • С точки зрения CLR, in, out и ref - это одно и то же; правила о том, кто и в какое время читает и записывает какие переменные, CLR не знает и не интересуется.
  • Поскольку именно CLR обеспечивает соблюдение правил о дисперсии, правила, применяемые к ref, также применяются к параметрам in и out.

Напротив, in и out в объявлениях параметров типа означают "этот параметр типа не должен использоваться ковариантным образом" и "этот параметр типа не должен использоваться контравариантным образом", соответственно.

Как отмечалось выше, мы выбрали in и out для этих модификаторов, потому что если мы видим IFoo, то T используется во "входных" позициях, а U - в "выходных". Хотя это не является строго верным, оно достаточно верно в 99,9% случаев использования, чтобы быть полезной мнемоникой.

К сожалению, интерфейс IFoo { void Foo(in T t, out U u); } является незаконным, потому что выглядит так, будто должен работать. Он не может работать, потому что с точки зрения верификатора CLR это оба параметра ref и, следовательно, они предназначены для чтения и записи.

Это просто одна из тех странностей, непредвиденные ситуации, когда две функции, которые по логике должны работать вместе, не работают вместе по причинам, связанным с деталями реализации.

46
ответ дан 1 December 2019 в 17:45
поделиться

Это означает, что у вас не может быть следующего объявления:

public delegate R MyDelegate<out R, in A>(ref A arg);

Изменить: @ Эрик Липперт поправил меня, что это все еще разрешено:

public delegate void MyDelegate<R, in A>(A arg, out R s);

Это действительно имеет смысл, поскольку общий параметр R не отмечен как вариант, чтобы не нарушать правила. Однако этот по-прежнему является незаконным:

public delegate void MyDelegate<out R, in A>(A arg, out R s);
1
ответ дан 1 December 2019 в 17:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: