Что такое «закрытие» в .NET?

Что такое закрытие ? У нас есть они в .NET?

Если они существуют в .NET, можете ли вы предоставить fragment кода (желательно на C #), объяснив это?

EDIT: Я просмотрел статью Джона Скита, чтобы понять, что такое закрытие и как использовать их в .NET.

12 Solutions collect form web for “Что такое «закрытие» в .NET?”

У меня есть статья по этой теме . (В нем много примеров.)

По сути, закрытие представляет собой блок кода, который может быть выполнен в более позднее время, но который поддерживает среду, в которой он был сначала создан, т. Е. Он все еще может использовать локальные переменные и т. Д. Метода, который его создал, даже после этого метод завершил выполнение.

Общая особенность закрытий реализована в C # анонимными методами и lambda-выражениями.

Вот пример использования анонимного метода:

using System; class Test { static void Main() { Action action = CreateAction(); action(); action(); } static Action CreateAction() { int counter = 0; return delegate { // Yes, it could be done in one statement; // but it is clearer like this. counter++; Console.WriteLine("counter={0}", counter); }; } } 

Вывод:

 counter=1 counter=2 

Здесь мы видим, что действие, возвращаемое CreateAction, по-прежнему имеет доступ к переменной счетчика и может даже увеличивать ее, хотя сам CreateAction завершен.

Если вы заинтересованы в том, как C # реализует Closure, читайте «Я знаю ответ (его 42) блог»,

Компилятор генерирует class в фоновом режиме для инкапсуляции метода anoymous и переменной j

 [CompilerGenerated] private sealed class <>c__DisplayClass2 { public <>c__DisplayClass2(); public void b__0() { Console.Write("{0} ", this.j); } public int j; } 

для функции:

 static void fillFunc(int count) { for (int i = 0; i < count; i++) { int j = i; funcArr[i] = delegate() { Console.Write("{0} ", j); }; } } 

Включение этого:

 private static void fillFunc(int count) { for (int i = 0; i < count; i++) { Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1(); class1.j = i; Program.funcArr[i] = new Func(class1.b__0); } } 

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

Для очень простого примера возьмите этот код C #:

  delegate int testDel(); static void Main(string[] args) { int foo = 4; testDel myClosure = delegate() { return foo; }; int bar = myClosure(); } 

В конце этого бара будет установлено значение 4, и делегат myClosure может быть передан для использования в другом месте программы.

Закрытие может использоваться для множества полезных вещей, таких как замедленное выполнение или упрощение интерфейсов. LINQ в основном строится с использованием закрытий. Самый непосредственный способ, который это пригодится большинству разработчиков, заключается в добавлении обработчиков событий к динамически созданным элементам управления – вы можете использовать блокировки для добавления поведения при создании экземпляра элемента управления, а не для хранения данных в другом месте.

 Func GetMultiplier(int a) { return delegate(int b) { return a * b; } } //... var fn2 = GetMultiplier(2); var fn3 = GetMultiplier(3); Console.WriteLine(fn2(2)); //outputs 4 Console.WriteLine(fn2(3)); //outputs 6 Console.WriteLine(fn3(2)); //outputs 6 Console.WriteLine(fn3(3)); //outputs 9 

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

Вот надуманный пример для C #, который я создал из аналогичного кода в JavaScript:

 public delegate T Iterator() where T : class; public Iterator CreateIterator(IList x) where T : class { var i = 0; return delegate { return (i < x.Count) ? x[i++] : null; }; } 

Итак, вот какой код, который показывает, как использовать вышеуказанный код ...

 var iterator = CreateIterator(new string[3] { "Foo", "Bar", "Baz"}); // So, although CreateIterator() has been called and returned, the variable // "i" within CreateIterator() will live on because of a closure created // within that method, so that every time the anonymous delegate returned // from it is called (by calling iterator()) it's value will increment. string currentString; currentString = iterator(); // currentString is now "Foo" currentString = iterator(); // currentString is now "Bar" currentString = iterator(); // currentString is now "Baz" currentString = iterator(); // currentString is now null 

Надеюсь, что это несколько полезно.

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

Вот простой пример:
List.Find метод может принять и выполнить часть кода (закрытие), чтобы найти элемент списка.

 // Passing a block of code as a function argument List ints = new List {1, 2, 3}; ints.Find(delegate(int value) { return value == 1; }); 

Используя синтаксис C # 3.0, мы можем записать это как:

 ints.Find(value => value == 1); 

Закрытие – это когда функция определена внутри другой функции (или метода) и использует переменные из родительского метода . Это использование переменных, которые находятся в методе и завернуты в определенную в нем функцию, называется замыканием.

У Марка Seemann есть несколько интересных примеров закрытия в его блоге, где он выполняет параллель между oop и функциональным программированием.

И чтобы сделать его более подробным

 var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);//when this variable Func read = id => { var path = Path.Combine(workingDirectory.FullName, id + ".txt");//is used inside this function return File.ReadAllText(path); };//the entire process is called a closure. 

Закрытие – это fragmentы кода, которые ссылаются на переменную вне себя (ниже под ними в стеке), которая может быть вызвана или выполнена позже (например, когда определено событие или делегат и может быть вызвано в какой-то неопределенный будущий момент времени ) … Поскольку внешняя переменная, что fragment ссылок на код может выйти из области действия (и в противном случае был бы утерян), тот факт, что на него ссылается кусок кода (называемый замыканием), указывает, что среда выполнения «удерживает «эта переменная в области видимости до тех пор, пока она больше не понадобится блокировкой кода …

Я тоже пытался понять это, а ниже – fragmentы кода для Same Code в Javascript и C #, показывающие закрытие.

  1. Count Количество времен, каждое событие произошло или не было нажато каждую кнопку.

JavaScript:

 var c = function () { var d = 0; function inner() { d++; alert(d); } return inner; }; var a = c(); var b = c();     

C #:

 using System.IO; using System; class Program { static void Main() { var a = new a(); var b = new a(); a.call(); a.call(); a.call(); b.call(); b.call(); b.call(); } } public class a { int b = 0; public void call() { b++; Console.WriteLine(b); } } 
  1. count Общее количество раз, когда произошло событие щелчка или общее количество кликов независимо от элемента управления.

JavaScript:

 var c = function () { var d = 0; function inner() { d++; alert(d); } return inner; }; var a = c();   

C #:

 using System.IO; using System; class Program { static void Main() { var a = new a(); var b = new a(); a.call(); a.call(); a.call(); b.call(); b.call(); b.call(); } } public class a { static int b = 0; public void call() { b++; Console.WriteLine(b); } } 

Просто из ничего, простой и понятный ответ из книги C # 7.0 в двух словах.

Предварительно необходимо знать : выражение lambda может ссылаться на локальные переменные и параметры метода, в котором он определен (внешние переменные).

  static void Main() { int factor = 2; //Here factor is the variable that takes part in lambda expression. Func multiplier = n => n * factor; Console.WriteLine (multiplier (3)); // 6 } 

Реальная часть : внешние переменные, на которые ссылается lambda-выражение, называются захваченными переменными. Выражение lambda, которое захватывает переменные, называется замыканием.

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

 int factor = 2; Func multiplier = n => n * factor; factor = 10; Console.WriteLine (multiplier (3)); // 30 

Если вы пишете встроенный анонимный метод (C # 2) или (желательно) выражение Lambda (C # 3 +), фактический метод все еще создается. Если этот код использует локальную переменную внешнего вида – вам все равно нужно передать эту переменную методу.

например, возьмите это предложение Linq Where (которое представляет собой простой метод расширения, который передает выражение lambda):

 var i = 0; var items = new List { "Hello","World" }; var filtered = items.Where(x => // this is a predicate, ie a Func written as a lambda expression // which is still a method actually being created for you in compile time { i++; return true; }); 

если вы хотите использовать i в этом выражении lambda, вы должны передать его этому созданному методу.

Итак, возникает первый вопрос: должен ли он передаваться по значению или ссылке?

Передача по ссылке (я думаю) более предпочтительна, поскольку вы получаете доступ для чтения / записи к этой переменной (и это то, что делает C #, я думаю, что команда в Microsoft взвесила все плюсы и минусы и отправилась с помощью ссылки; по словам Джона Скита статья , Java пошла с по значению).

Но затем возникает другой вопрос: где выделить это?

Должно ли это на самом деле / ​​естественно выделяться в стеке? Ну, если вы разместите его в стеке и передадите его по ссылке, могут возникнуть ситуации, когда он переживает свой собственный стек стека. Возьмем следующий пример:

 static void Main(string[] args) { Outlive(); var list = whereItems.ToList(); Console.ReadLine(); } static IEnumerable whereItems; static void Outlive() { var i = 0; var items = new List { "Hello","World" }; whereItems = items.Where(x => { i++; Console.WriteLine(i); return true; }); } 

Выражение lambda (в предложении Where) снова создает метод, который ссылается на i. Если i выделено в стеке Outlive, то к тому времени, когда вы перечислите whereItems, я, используемый в сгенерированном методе, будет указывать на i Outlive, то есть на место в стеке, которое больше не доступно.

Хорошо, поэтому нам нужно это на куче.

Итак, что делает компилятор C # для поддержки этого встроенного анонимного / lambda, используется то, что называется « Закрытие »: он создает class в вызываемой ( а не плохой ) оболочке DisplayClass, содержащей поле i, и функцию, которая фактически использует Это.

Что-то, что было бы эквивалентно этому (вы можете увидеть IL, сгенерированный с использованием ILSpy или ILDASM):

 class <>c_DisplayClass1 { public int i; public bool b__0() { this.i++; Console.WriteLine(i); return true; } } 

Он создает экземпляр этого classа в локальной области действия и заменяет любой код, относящийся к i или lambda-выражению, экземпляром закрытия. Таким образом – в любое время, когда вы используете i в своем «локальном объеме», где я был определен, вы фактически используете это поле экземпляра DisplayClass.

Поэтому, если бы я изменил «локальный» i в основном методе, он фактически изменит _DisplayClass.i;

т.е.

 var i = 0; var items = new List { "Hello","World" }; var filtered = items.Where(x => { i++; return true; }); filtered.ToList(); // will enumerate filtered, i = 2 i = 10; // i will be overwriten with 10 filtered.ToList(); // will enumerate filtered again, i = 12 Console.WriteLine(i); // should print out 12 

он будет распечатывать 12, так как «i = 10» переходит в это поле dispalyclass и изменяет его непосредственно перед вторым перечислением.

Хорошим источником по этой теме является модуль Bart De Smet Pluralsight (требуется регистрация) (также игнорирует его ошибочное использование термина «Подъем» – что (я думаю), он означает, что локальная переменная (т.е. i) изменена на ссылку в новое поле DisplayClass).


В других новостях, похоже, есть некорректное представление о том, что «Closures» связаны с циклами – как я понимаю, «Closures» – это не понятие, связанное с циклами , а скорее с анонимными методами / lambda-выражениями использования локальных переменных с областью – хотя некоторые трюки вопросы используют циклы, чтобы продемонстрировать это.

Закрытие – это функция, определенная внутри функции, которая может обращаться к локальным переменным, а также к его родительскому элементу.

 public string GetByName(string name) { List theThings = new List(); return theThings.Find(t => t.Name == name)[0]; } 

поэтому функция внутри метода поиска.

  t => t.Name == name 

может получить доступ к переменным внутри своей области, t и имени переменной, которое находится в области его родителей. Несмотря на то, что он выполняется методом find как делегат, из другой области все вместе.

Interesting Posts

MPI_Bcast динамический массив 2d

Как центрировать ячейки в JTable

Производительность ConcurrentHashmap против HashMap

Роль антистатической обертки для запястий, применение термического раствора, очистка при проливании и другие необходимые меры предосторожности, защита вашей материнской платы

JQuery – сохранение ответа ajax в глобальной переменной

Остановить все активные запросы ajax в jQuery

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

Полиморфная ассоциация Rails с несколькими ассоциациями на одной модели

Vc_redist.x86.exe Ошибка установки: 0x80240017 Неопределенная ошибка

Объявлять переменные в верхней части функции или в отдельных областях?

Windows 8 Hyper-V – как предоставить доступ в Интернет через Интернет?

Как эффективно рассчитать расстояние между парой координат с помощью data.table: =

Как установить шрифт Windows с помощью C #

Доступ к камере с iPhone SDK

Как изменить хром-пакетный идентификатор приложения Или Почему нам нужно ключевое поле в манифесте. Json?

Давайте будем гением компьютера.