Wpis z mikrobloga

Ok. Ostatnio Visual Studio rzuciło mi błędem "Cannot use ref or out parameter 'x' inside an anonymous method, lambda expression, or query expression". Nigdy wcześniej się z tym nie spotkałem, więc postanowiłem to zbadać.

Okazuje się, że wyrażenia lambda mają swój własny cykl życia zmiennych. Zmienne są trzymane w wyrażeniach lambda, nawet wtedy, kiedy ich zakres powinien się już skończyć i GC powinien je zgarnąć. Dlatego, gdybyśmy przekazali zmienną przez ref, moglibyśmy kiedyś zakończyć jej życie, a lambda nie mogłaby się do niej odwoływać. Lekko zmodyfikowałem przykład z MSDNu na swoje potrzeby, żeby pokazać, jak to działa:


    
delegate bool BoolDel();
    
delegate int IntDel();
``````
    
class Program
    {
        BoolDel boolDel;
        IntDel intDel;
``````
        
public void TestMethod(int x)
        {
            
int y = 1;
``````
            //Inicjalizacja delegatów
            //boolDel zostanie wywołany 2 razy poza TestMethod do sprawdzenia, 
            // czy nasze zmienne są równe
            boolDel = () => { 
return y == x; };
``````
            //intDel zostanie wywołany raz w środku TestMethod i raz poza nią
            intDel = () => { y *= 2; 
return y; };
``````
            
var result = intDel();
``````
            //Wyjście: y:2
            Console.WriteLine(
"y:{0}", result);
        }
``````
        
static void Main(string[] args)
        {
            
int x = 4;
            
var program = new Program();
``````
            program.TestMethod(x);
``````
            //Wywołanie boolDel już powinno wystarczyć jako dowód na to, 
            // że y nie zakończyło swojego zakresu wraz z końcem TestMethod()
            
var condition = program.boolDel();
``````
            //Wynik jest prosty, przecież x: 4, y: 2
            //Wyjście: condition:false
            Console.WriteLine(
"condition:{0}", condition);
``````
            //Kolejne wywołanie intDel to dowód, że lambda przechowuje
            // referencję do zmiennej, a nie jej kopię.
            
var result = program.intDel();
``````
            //Wyjście: y:4
            Console.WriteLine(
"y:{0}", result);
``````
            //Zmieniam x. Nie ma to znaczenia teraz,
            // ponieważ wiem i tak, co uzyskam w kolejnym wywołaniu boolDel,
            // ale przyda się w dalszej części moich zabaw.
            x = 10;
``````
            condition = program.boolDel();
``````
            //Do metody TestMethod podaliśmy x o wartości 4
            // i lambda przechowuje referencję do kopii x z TestMetchod(),
            // a nie referencję do x.
            //Wyjście: condition:true
            Console.WriteLine(
"condition:{0}", condition);
``````
            Console.ReadKey();
        }
    }

To mój pierwszy taki wpis, więc dajcie znać, czy się podoba, czy się nadaje do #programowanie, czy nie zbyt oczywiste, bo może wszyscy czytają MSDN od deski do deski i to wiedzą, a tylko ja się nie spotkałem, czy wgl zawracać sobie głowę itd. Jak gdzieś się mylę, to poprawiać koniecznie.

#csharp #maavdevelopment #justjuniorthings
  • 2
@Maav: Nie udało mi się doprowadzić do działania tego na refach. (W sumie nie potrzebowałem tego refa, więc mogłem go wyrzucić w moim kodzie i się nie przejmować). Za to pobawiłem się chwilę i ze wskaźnikami już działa. Może jeszcze w wolnym czasie postaram się to zepsuć, żeby rzuciło mi wyjątkiem czy coś.


        
unsafe public void TestMethod(int* x)
        {
            
int y = 1;
            boolDel = () => { 
return y == *x; };
            intDel = () => { y *= 2; 
return