Кога се оценяват свойствата в затваряния?

Няколко метода в нашата кодова база използват „MaybeObject“, който може да бъде предаден във функции, когато резултатът може да е известен или може да разчита на извикване на външна уеб услуга, което все още не е извършено. Например, свойството по-долу може или да има определена известна стойност, или ако не е посочено и извикано след приключване на асинхронното извикване, то ще върне резултата от асинхронното извикване.

private string _internalString;
public string stringProp
{ 
    get
    {
        if (!string.IsNullOrEmpty(_internalString))
            return _internalString;
        return resultOfAsyncCallFromSomewhereElse;
    }

    set { _internalString = value; }
}

Очевидно опитът за препращане към свойството, преди асинхронното извикване да е завършило, би довел до изключение за нулева препратка, така че имаме и флаг за проверка дали стойността е налична.

Въпросът е дали в кода по-долу създаването на ламбда ще се опита и оцени stringProp (което може все още да не е попълнено), или оценката ще бъде отложена, докато не бъде извикано полученото действие (което ще бъде след проверка, че асинхронната операция е завършена) ?

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}

person mavnn    schedule 28.11.2011    source източник
comment
Предполагам, че не можете да сте сигурни и че в спецификациите и двете са валидни ситуации. Нека да видим какво казват пълните отговори.   -  person CodingBarfield    schedule 28.11.2011


Отговори (5)


Оценяването ще бъде отложено, докато не бъде извикано произтичащото действие.

Кодът, посочен от делегат, се изпълнява само когато самият делегат е изрично извикан, независимо как е създаден самият делегат.

Например тези начини за предаване на кода за изпълнение чрез Действие всички делегати са еквивалентни и методът doSomethingWithString няма да бъде изпълнен, докато не бъде направено извикване на Action():

Явен метод (.NET 1.1):

private MaybeObject maybe;

public Action ExampleMethod()
{
    return new Action(DoSomethingWithMaybeObject);
}

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}

Анонимен метод (.NET 2.0):

public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}

Lambda (.NET 3.5):

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}

Вижте също:

person Enrico Campidoglio    schedule 28.11.2011

В C# всичко ще бъде оценено по време на изпълнение, тъй като C# lambda Closures не е истинско затваряне, което може да разреши състояние на уловена среда в момента на създаване.

() => doSomethingWithString(maybe.stringProp);

Тук дори референцията maybe може да бъде нула и няма да имате проблеми като NullReferenceException, докато не изпълните делегат. Този трик понякога се използва за разрешаване на късно свързана стойност.

Уикипедия: Закриване:

Затварянето запазва препратка към средата по времето, когато е създадено (например към текущата стойност на локална променлива в обхващащия обхват), докато генеричната анонимна функция не трябва да прави това.

Добър преглед на спецификата на C# Closure - Анонимни методи и затваряния в C#

От дефиницията на Closure може да се заключи, че Closure помни стойностите на променливите по време на създаването си. Въпреки това в C# външната локална променлива (i в този случай) се споделя с анонимния метод чрез създаването му в купчината. Това означава, че всяка промяна в него в анонимния метод променя първоначалната стойност и когато методът се извика втори път, той получава модифицираната стойност на i като 1 (вижте втория ред на изхода). Това кара мнозина да твърдят, че анонимният метод всъщност не е Closure, тъй като по неговата дефиниция стойността на променлива по време на създаването на Closure трябва да се помни и да не може да се променя.

person sll    schedule 28.11.2011
comment
+1 „не е истинско затваряне“ - интересен момент, никога преди не съм го мислил така. - person Kirk Broadhurst; 28.11.2011

Свойствата в затварянията не се оценяват, докато не се изпълни действието, тъй като свойствата са методи, а не стойности. Достъпът до MyObject.MyProperty е като извикване на кода MyObject.get_MyProperty(), така че можете да получите различно поведение при използване на свойство спрямо използване на основната променлива.

person AndyClaw    schedule 25.09.2012

ще бъде отложено. ExampleMethod() връща делегат към анонимната функция () => doSomethingWithString(maybe.stringProp), така че doSomethingWithString() няма да бъде извикан, докато този делегат не бъде извикан

person Arash    schedule 28.11.2011

Трябва да опитате това сами, за да видите какво ще се случи!

В този случай изглежда, че затварянето наистина е около MaybeObject, а не само свойството на низа (защото обектът се предава на действието). Свойството на низа не е достъпно, докато действието не бъде изпълнено.

Но дори ако самият низ беше променливата, дадена на затварянето...

string s = "";
Func<bool> isGood = () => s == "Good"; 

s = "Good";
Console.WriteLine(isGood());

// prints 'True'

Тук можете да видите, че затварянето се извършва около низ, но не се оценява до изпълнението.

Делегатите работят точно като всяка друга функция - те всъщност не правят нищо, докато не бъдат извикани.

person Kirk Broadhurst    schedule 28.11.2011