سؤال

ما هو إغلاق?هل لدينا في .الشبكة ؟


إذا كانت موجودة في .صافي ، هل يمكن أن يرجى تقديم مقتطف الشفرة (يفضل أن يكون في C#) شرح ذلك ؟


تحرير:ذهبت من خلال جون السكيت المادة لفهم ما الإغلاق و كيفية استخدامها .صافي.

هل كانت مفيدة؟

المحلول

مقالا عن هذا الموضوع جدا. (انها لديها الكثير من الأمثلة).

في جوهرها، الإغلاق هو كتلة من التعليمات البرمجية التي يمكن تنفيذها في وقت لاحق، ولكن الذي يحافظ على البيئة التي أنشئت لأول مرة - أي أنه يمكن الاستمرار في استخدام المتغيرات المحلية وغيرها من الطريقة التي خلقها، حتى بعد انتهاء تلك الطريقة المنفذة.

ويتم تطبيق سمة عامة من سمات الإغلاق في C # بطرق مجهولة المصدر والعبارات امدا.

وهنا مثال باستخدام أسلوب مجهول:

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 # تطبيقات اختتام قراءة <لأ href = "http://blogs.msdn.com/b/abhinaba/archive/2005/10/18/482180.aspx" يختلط = "نوفولو noreferrer ">" أنا أعرف الجواب (من أصل 42) بلوق "

ووالبرمجي فئة في الخلفية لتغليف طريقة anoymous وي متغير

[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
    public <>c__DisplayClass2();
    public void <fillFunc>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.<fillFunc>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<int, int> 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 # الذي خلقته من قانون مماثل في جافا سكريبت:

public delegate T Iterator<T>() where T : class;

public Iterator<T> CreateIterator<T>(IList<T> 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<int> ints = new List<int> {1, 2, 3};
ints.Find(delegate(int value) { return value == 1; });

وعن طريق C # 3.0 في بناء الجملة يمكن أن نكتب هذا على النحو التالي:

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

والإغلاق عند تعريف وظيفة داخل وظيفة (أو طريقة) آخر ويستخدم المتغيرات من <م> طريقة الأم . وهذا ما يسمى استخدام المتغيرات التي تقع في طريقة وملفوفة في وظيفة محددة داخلها، <م> إغلاق.

وكافة سيمان لديه بعض الأمثلة المثيرة للاهتمام في الإغلاق في <لأ href = "http://blog.ploeh.dk/2014/03/10/solid-the-next-step-is-functional/" يختلط = "نوفولو noreferrer "> له بلوق وظيفة حيث يقوم paralel بين ووب والبرمجة الوظيفية.

ولجعلها أكثر تفصيلا

var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);//when this variable
Func<int, string> 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.

والإغلاق وقطع من التعليمات البرمجية التي تشير إلى متغير خارج أنفسهم، (من تحتها على المكدس)، الذي يمكن تسميته أو أعدم في وقت لاحق، (مثل عندما يتم تعريف حدث أو مندوب، ويمكن الحصول على تسمى في بعض المستقبل إلى أجل غير مسمى نقطة في الوقت المناسب) ... لأن المتغير الخارجي أن قسما من المراجع كود قد خرجت من نطاق (وعلى خلاف ذلك قد فقدت)، والحقيقة المشار إليها من قبل جزء من التعليمات البرمجية (يسمى إغلاق) يحكي وقت التشغيل إلى "عقد" هذا المتغير في النطاق حتى لم يعد هناك حاجة من قبل شريحة إغلاق رمز ...

لقد كنت أحاول أن أفهم ذلك أيضا, حسنا وفيما يلي مقتطفات الشفرة على نفس التعليمات البرمجية في جافا سكريبت و C# عرض الإغلاق.

  1. عد عدد مرات كل حدث حدث أو أي من مرات كل زر النقر.

جافا سكريبت :

var c = function ()
{
    var d = 0;

    function inner() {
      d++;
      alert(d);
  }

  return inner;
};

var a = c();
var b = c();

<body>
<input type=button value=call onClick="a()"/>
  <input type=button value=call onClick="b()"/>
</body>

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. عد إجمالي عدد مرات انقر فوق الحدث حدث أو العد إجمالي عدد النقرات بغض النظر عن نطاق السيطرة.

جافا سكريبت:

var c = function ()
{
    var d = 0;

    function inner() {
     d++;
     alert(d);
  }

  return inner;
};

var a = c();

<input type=button value=call onClick="a()"/>
  <input type=button value=call onClick="a()"/>

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.

على ما قبل requisit يجب أن نعرف : لتعبير امدا يمكن الرجوع المتغيرات والمعلمات المحلية من طريقة حيث انها محددة (المتغيرات الخارجية).

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

<القوي> جزء ريال مدريد : لمتغيرات الخارجي المشار إليه من قبل تسمى المتغيرات القبض على التعبير امدا. ويطلق تعبير لامدا الذي يلتقط متغيرات الإغلاق.

<قوية> النقطة الأخيرة أن يكون لاحظت : ليتم تقييم المتغيرات التي تم التقاطها عندما يتم استدعاء مندوب في الواقع، وليس عندما تم القبض على المتغيرات:

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

إذا كنت أكتب مضمنة طريقة مجهول (C#2) أو (يفضل) التعبير امدا (#C 3+), فعلي الأسلوب لا يزال يجري إنشاؤها.إذا كان هذا هو رمز باستخدام الخارجي النطاق المتغير المحلي لا تزال تحتاج إلى تمرير هذا المتغير إلى الأسلوب بطريقة أو بأخرى.

على سبيل المثالخذ هذا Linq حيث البند (الذي هو امتداد بسيط الأسلوب الذي يمر التعبير امدا):

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

إذا كنت ترغب في استخدام في هذا التعبير امدا, عليك أن تمر عليه إلى أن إنشاء طريقة.

لذا فإن السؤال الأول الذي يطرح نفسه هو: يجب أن يتم تمريرها حسب القيمة أو مرجع ؟

تمر الإشارة (اعتقد) أكثر من الأفضل كما يمكنك الحصول على حق الوصول للقراءة/الكتابة إلى أن متغير (و هذا ما C# لا ؛ أعتقد أن الفريق في Microsoft وزن إيجابيات وسلبيات وذهب مع المرجعية ؛ وفقا جون السكيت المادة, جافا ذهب مع قيمة).

ولكن ثم سؤال آخر يطرح نفسه: حيث أن تخصيص هذا ؟

ينبغي في الواقع/من الطبيعي أن توزع على كومة ؟ حسنا, إذا كنت وإحالته على المكدس وتمريرها من قبل المرجعية ، يمكن أن يكون هناك حالات حيث أنها تعيش أطول من أنها تملك الإطار المكدس.خذ هذا المثال:

static void Main(string[] args)
{
    Outlive();
    var list = whereItems.ToList();
    Console.ReadLine();
}

static IEnumerable<string> whereItems;

static void Outlive()
{
    var i = 0;
    var items = new List<string>
    {
        "Hello","World"
    };            
    whereItems = items.Where(x =>
    {
        i++;
        Console.WriteLine(i);
        return true;
    });            
}

لامدا التعبير (في جملة Where) مرة أخرى يخلق الأسلوب الذي يشير إلى أنا.إذا لم يتم تخصيص كومة من تعمر ، ثم بحلول الوقت الذي كنت تعداد whereItems ، كنت في الأسلوب الذي تم إنشاؤه سوف أشير إلى أنني من تعمر ، أيإلى مكان في المكدس الذي لم يعد موجودا.

حسنا, لذلك نحن في حاجة إليها على كومة ثم.

فما C# compiler يقوم بدعم هذه مضمنة مجهول/لامبدا ، هو استخدام ما يسمى "الإغلاق":فإنه يخلق فئة على كومة يسمى (ضعيف نوعا ما) DisplayClass الذي يحتوي على الحقل الذي يحتوي على لا وظيفة أن يستخدم في الواقع.

شيء من شأنه أن يكون معادلا هذا (يمكنك أن ترى IL إنشاؤها باستخدام ILSpy أو ILDASM):

class <>c_DisplayClass1
{
    public int i;

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

فإنه instantiates تلك الفئة في النطاق المحلي ، ويحل محل أي رمز المتعلقة أنا أو امدا التعبير مع أن إغلاق سبيل المثال.إذا في أي وقت كنت تستخدم اي في "النطاق المحلي" رمز أين كنت تحديدا ، كنت فعلا باستخدام هذا DisplayClass سبيل المثال المجال.

حتى لو تغيير "المحلية:" أنا في الأسلوب الرئيسي ، فإنه سيتم تغيير الواقع _DisplayClass.الأول ؛

أي

var i = 0;
var items = new List<string>
{
    "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 المجال التغييرات قبل 2nd التعداد.

مصدر جيد على الموضوع هذا بارت دي سميت Pluralsight وحدة (يتطلب التسجيل) (أيضا تجاهل له الخاطئة استخدام مصطلح "الرفع" - ما (أعتقد) أنه يعني أن المتغير المحلي (أيط) يتم تغيير إلى الرجوع إلى الجديد DisplayClass الميدانية).


في أخبار أخرى, يبدو أن هناك اعتقاد خاطئ بأن "الإغلاق" هي ذات الصلة إلى حلقات - كما أفهم "الإغلاق" غير مفهوم المتصلة الحلقات, بل وسائل المجهول / امدا تستخدم التعبيرات المحلية متغيرات راقب - على الرغم من أن بعض الأسئلة خدعة استخدام الحلقات لإثبات ذلك.

والإغلاق وظيفة، تعريف داخل دالة، التي يمكن الوصول إلى المتغيرات المحلية منها وكذلك الأم.

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

وهكذا وظيفة داخل طريقة البحث.

 t => t.Name == name

ويمكن الوصول إلى المتغيرات داخل نطاقه، تي، واسم المتغير الذي هو في نطاق والديه. على الرغم من أن يتم تنفيذ ذلك من خلال طريقة الاكتشاف كمندوب، من نطاق آخر كل ذلك معا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top