Почему нельзя «это» для типов значений в штучной упаковке? - PullRequest
0 голосов
/ 12 марта 2019

Итак, я хотел иметь возможность имитировать функциональность «с» VB в C # и через StackOverflow наткнулся на довольно умное решение:

public static x with<x>(this x item, Func<x,x> f)
{
    item = f(item);
    return item;
}

, чтобы реализовать, вы бы сделали что-то вроде:

myObject = myObject.with(myVariable => {
    //do things
});

Однако, я столкнулся с проблемой, когда попытался реализовать это внутри структуры с одним из полей структуры.В нем говорилось, что " анонимные методы [...] внутри структур не могут получить доступ к членам" this "[...] ."

Я провел некоторое исследование по этому вопросу и нашел ответ на этот вопрос , который в конечном итоге утверждает, что «это» для типов значений не может быть заключено в квадрат.После изучения того, что означает бокс для C #, это имеет смысл, учитывая, что параметр для функции не имеет определенного типа.

Мой вопрос: почему не может "это"для типов значений быть в штучной упаковке?

Ответы [ 2 ]

1 голос
/ 13 марта 2019

Суть в том, что для метода на struct значение this - это не значение (т. Е. Значение вашего SomeStruct), а скорее ссылка (ref SomeStruct или, по сути, in SomeStruct в случае readonly struct).

Вы не можете пометить управляемый указатель этой формы - это неСценарий поддерживается во время выполнения.Управляемые указатели предназначены только для стека.Фактически, в настоящее время вы не можете даже иметь поле ref SomeStruct в пользовательском ref struct, которое не может выйти из стека.

Компилятор может обмануть путем притворства для этого - то есть путем разыменования управляемого указателя из ref SomeStruct в SomeStruct и создания контекста захвата, где this интерпретируется как "SomeStruct, который мы разыменовали ранее", но ... тогда компилятор не может гарантировать такое же поведение и результаты (на самом деле, я подозреваю, что для этого можно было бы сделать сценарий readonly struct, но ... вероятно, легче не вводить этот тонкийРазличие).

Вместо этого, компилятор предлагает вам эффективно выполнить вышеуказанный шаг вручную ;поскольку компилятор больше не работает в терминах this, ему больше не нужно притворяться, что он уважает обычные результаты для работы с this, и вместо этого он должен только гарантировать поведение явно разыменованной копии стоимости.Вот почему он советует (по крайней мере, в текущих версиях компилятора):

Попробуйте скопировать 'this' в локальную переменную вне анонимного метода, лямбда-выражения или выражения запроса и использовать вместо этого локальный.

Большинство лямбд, локальных методов и т. Д., Следовательно, могут быть достигнуты с помощью прагматического шага:

MyStruct copy = this; // dereference

затем в вашем лямбда / локальном методе / etc: вместо касания Something aka this.Something - коснитесь copy.Something.Теперь только copy включается в контекст захвата, и copy не связан правилами ref SomeStruct (потому что: это не ref SomeStruct - это SomeStruct).

Это означает , однако, означает, что если ваше намерение было мутировать this (и иметь этот видимый на месте , скореечем в качестве возвращаемого значения), то это не будет работать.Вы будете только мутировать копию (то есть copy).Это именно то, что компилятор должен был бы сделать в любом случае , если бы он солгал, но, по крайней мере, теперь шаг копирования (разыменование) является явным и видимым в вашем коде.

1 голос
/ 12 марта 2019

Полагаю, вы знаете о настройке свойств во время построения, которая похожа (и более идиоматична для c #)?

class MySpecialClass
{
   public string Property1 {get;set;}
   public int Length {get;set;}
   public float Width {get;set;}
}

var p = new MySpecialClass
{
  Property1 = "PropertyValue",
  Length = 12,
  OtherThing = 1.234F
};
...