এই অবজেক্টটি আজীবন-এক্সটেন্ডিং-ক্লোজারটি কি সি # সংকলক বাগ রয়েছে?


136

আমি যখন সি # সংকলকের অংশে কিছু অত্যন্ত কৌতূহলী কোড-জেনের মধ্যে দৌড়ালাম তখন অবৈধ-জীবনকাল বাড়ানোর (বৈধভাবে) সম্ভাবনা সম্পর্কে একটি প্রশ্নের উত্তর দিচ্ছিলাম ( যদি তা বিবেচিত হয় তবে 4.0)।

আমি যে সংক্ষিপ্ততম নিন্দা পেতে পারি তা নিম্নলিখিত:

  1. এমন একটি ল্যাম্বডা তৈরি করুন যা ধারণকারী ধরণের স্ট্যাটিক পদ্ধতিতে কল করার সময় কোনও স্থানীয়কে ক্যাপচার করে ।
  2. উত্পাদিত প্রতিনিধি-রেফারেন্সটি ধারণকৃত অবজেক্টের উদাহরণ ক্ষেত্রের জন্য বরাদ্দ করুন ।

ফলাফল: সংকলকটি একটি ক্লোজার-অবজেক্ট তৈরি করে যা ল্যাম্বডা তৈরির বিষয়টি উল্লেখ করে, যখন তার কোনও কারণ নেই - প্রতিনিধিটির 'অভ্যন্তরীণ' লক্ষ্য একটি স্থির পদ্ধতি, এবং ল্যাম্বডা-তৈরি-অবজেক্টের উদাহরণ সদস্যদের প্রয়োজন হয় না comp ডেলিগেটের মৃত্যুদণ্ড কার্যকর হওয়ার পরে স্পর্শকাতর হন (এবং না)। কার্যকরভাবে, সংকলক প্রোগ্রামার যেমনটি thisকারণ ছাড়াই ক্যাপচার করেছে হিসাবে কাজ করছে ।

class Foo
{
    private Action _field;

    public void InstanceMethod()
    {
        var capturedVariable = Math.Pow(42, 1);

        _field = () => StaticMethod(capturedVariable);
    }

    private static void StaticMethod(double arg) { }
}

একটি রিলিজ বিল্ড থেকে উত্পন্ন কোড ('সরল' সি # তে বিভক্ত) এর মত দেখাচ্ছে:

public void InstanceMethod()
{

    <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();

    CS$<>8__locals2.<>4__this = this; // What's this doing here?

    CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
    this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
    // Fields
    public Foo <>4__this; // Never read, only written to.
    public double capturedVariable;

    // Methods
    public void <InstanceMethod>b__0()
    {
        Foo.StaticMethod(this.capturedVariable);
    }
}

মান্য যে <>4__thisঅবসান বস্তুর ক্ষেত্র একটি বস্তু রেফারেন্স সহ জনবহুল হয় কিন্তু থেকে (কোন কারন নেই) পড়ুন করা হয় না।

তাহলে এখানে কি হচ্ছে? ভাষা-নির্দিষ্টকরণ কি এর জন্য অনুমতি দেয়? এটি কি কম্পাইলার বাগ / অদ্ভুততা বা অবজেক্টের উল্লেখের জন্য বন্ধ হওয়ার কোনও ভাল কারণ আছে (যা আমি স্পষ্টভাবে অনুপস্থিত)? এটি আমাকে উদ্বিগ্ন করে তোলে কারণ এটি ক্লোজার-হ্যাপি প্রোগ্রামারগুলির (যেমন আমার মতো) অজান্তেই অদ্ভুতভাবে স্মৃতি-ফাঁস (প্রোগ্রামটিকে হ্যান্ডলার হিসাবে ডেলিগেট হিসাবে ব্যবহার করা হত) কল্পনা করার জন্য একটি রেসিপি বলে মনে হয়।


19
মজাদার. আমার কাছে বাগের মতো মনে হচ্ছে। মনে রাখবেন আপনি যদি কোনও উদাহরণ ক্ষেত্রের জন্য বরাদ্দ না করেন (যেমন আপনি যদি মানটি ফেরৎ দেন), এটি ক্যাপচার করে নাthis
জন স্কিটি

15
আমি এটি ভিএস 11 বিকাশকারী পূর্বরূপ দিয়ে repro করতে পারবেন না। VS2010SP1 এ তিরস্কার করতে পারেন। দেখে মনে হচ্ছে এটি স্থির হয়েছে :)
লেপি

2
এটি VS2008SP1 এও ঘটে। VS2010SP1 এর ক্ষেত্রে এটি 3.5 এবং 4.0 উভয়ের জন্যই ঘটে।
লেপি

5
হুম, বাগ এটি প্রয়োগ করার জন্য একটি ভয়ঙ্কর বড় শব্দ। সংকলকটি কিছুটা অক্ষম কোড উত্পন্ন করে। অবশ্যই কোনও ফুটো নয়, এই আবর্জনা কোনও সমস্যা ছাড়াই সংগ্রহ করে। তারা যখন অ্যাসিঙ্ক বাস্তবায়নে কাজ করেছিল তখন সম্ভবত এটি স্থির হয়ে গেছে।
হান্স প্যাস্যান্ট

7
@ হাসান, প্রতিনিধি যদি বস্তুর আজীবন বেঁচে থাকতে পারে তবে এটি কোনও সমস্যা ছাড়াই জঞ্জাল সংগ্রহ করবে না এবং এটি ঘটতে বাধা দেওয়ার মতো কিছুই নেই।
সফটমিমেস

উত্তর:


24

এটি নিশ্চিত একটি বাগের মতো দেখাচ্ছে। আমার মনোযোগ এটি আনয়ন করার জন্য ধন্যবাদ। আমি এটার দিকে নজর রাখব. এটি ইতিমধ্যে এটি সন্ধান করা এবং স্থির করা সম্ভব।


7

এটি একটি বাগ বা অপ্রয়োজনীয় বলে মনে হচ্ছে:

আমি আপনাকে আইএল ল্যাংয়ের উদাহরণ দিচ্ছি:

.method public hidebysig 
    instance void InstanceMethod () cil managed 
{
    // Method begins at RVA 0x2074
    // Code size 63 (0x3f)
    .maxstack 4
    .locals init (
        [0] class ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'   'CS$<>8__locals2'
    )

    IL_0000: newobj instance void ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::.ctor()
    IL_0005: stloc.0
    IL_0006: ldloc.0
    IL_0007: ldarg.0
    IL_0008: stfld class ConsoleApplication1.Program/Foo ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::'<>4__this' //Make ref to this
    IL_000d: nop
    IL_000e: ldloc.0
    IL_000f: ldc.r8 42
    IL_0018: ldc.r8 1
    IL_0021: call float64 [mscorlib]System.Math::Pow(float64, float64)
    IL_0026: stfld float64 ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::capturedVariable
    IL_002b: ldarg.0
    IL_002c: ldloc.0
    IL_002d: ldftn instance void ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::'<InstanceMethod>b__0'()
    IL_0033: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
    IL_0038: stfld class [mscorlib]System.Action ConsoleApplication1.Program/Foo::_field
    IL_003d: nop
    IL_003e: ret
} // end of method Foo::InstanceMethod

উদাহরণ 2:

class Program
{
    static void Main(string[] args)
    {
    }


    class Foo
    {
        private Action _field;

        public void InstanceMethod()
        {
            var capturedVariable = Math.Pow(42, 1);

            _field = () => Foo2.StaticMethod(capturedVariable);  //Foo2

        }

        private static void StaticMethod(double arg) { }
    }

    class Foo2
    {

        internal static void StaticMethod(double arg) { }
    }


}

ক্লাবে: (নোট !! এখন এই রেফারেন্সটি চলে গেছে!)

public hidebysig 
        instance void InstanceMethod () cil managed 
    {
        // Method begins at RVA 0x2074
        // Code size 56 (0x38)
        .maxstack 4
        .locals init (
            [0] class ConsoleApplication1.Program/Foo/'<>c__DisplayClass1' 'CS$<>8__locals2'
        )

        IL_0000: newobj instance void ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::.ctor()
        IL_0005: stloc.0
        IL_0006: nop //No this pointer
        IL_0007: ldloc.0
        IL_0008: ldc.r8 42
        IL_0011: ldc.r8 1
        IL_001a: call float64 [mscorlib]System.Math::Pow(float64, float64)
        IL_001f: stfld float64 ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::capturedVariable
        IL_0024: ldarg.0 //No This ref
        IL_0025: ldloc.0
        IL_0026: ldftn instance void ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::'<InstanceMethod>b__0'()
        IL_002c: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
        IL_0031: stfld class [mscorlib]System.Action ConsoleApplication1.Program/Foo::_field
        IL_0036: nop
        IL_0037: ret
    }

উদাহরণ 3:

class Program
{
    static void Main(string[] args)
    {
    }

    static void Test(double arg)
    {

    }

    class Foo
    {
        private Action _field;

        public void InstanceMethod()
        {
            var capturedVariable = Math.Pow(42, 1);

            _field = () => Test(capturedVariable);  

        }

        private static void StaticMethod(double arg) { }
    }


}

আইএল-তে: (এই পয়েন্টারটি ফিরে এসেছে)

IL_0006: ldloc.0
IL_0007: ldarg.0
IL_0008: stfld class ConsoleApplication1.Program/Foo ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::'<>4__this' //Back again.

এবং তিনটি ক্ষেত্রেই পদ্ধতি- b__0 () - একই চেহারা:

instance void '<InstanceMethod>b__0' () cil managed 
    {
        // Method begins at RVA 0x2066
        // Code size 13 (0xd)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld float64 ConsoleApplication1.Program/Foo/'<>c__DisplayClass1'::capturedVariable
                   IL_0006: call void ConsoleApplication1.Program/Foo::StaticMethod(float64) //Your example
                    IL_0006: call void ConsoleApplication1.Program/Foo2::StaticMethod(float64)//Example 2
        IL_0006: call void ConsoleApplication1.Program::Test(float64) //Example 3
        IL_000b: nop
        IL_000c: ret
    }

এবং সমস্ত 3 ক্ষেত্রে একটি স্থির পদ্ধতির রেফারেন্স রয়েছে, সুতরাং এটি এটিকে আরও বিজোড় করে তোলে। সুতরাং এই লিটল অ্যানালাইসের পরে, আমি এটি একটি বাগ / কোনও ভাল করার জন্য বলব। !


আমি মনে করি এর অর্থ নেস্টেড শ্রেণীর দ্বারা উত্পন্ন ল্যাম্বডা অভিব্যক্তির ভিতরে পিতামাতার ক্লাস থেকে স্থির পদ্ধতিগুলি ব্যবহার করা কোনও BAD ধারণা? আমি কেবল ভাবছি যদি Foo.InstanceMethodস্থির হয় তবে এটি কি রেফারেন্সটি সরিয়ে দেবে? আমি জানার জন্য কৃতজ্ঞ হতে হবে।
Ivaylo স্লাভভ

1
@ আইভায়লো: যদি স্থিরও থাকতেন তবে নজরে আসার মতো Foo.InstanceMethodনজির থাকত না, এবং তাই কোনওভাবেই বন্ধের দ্বারা ধরা পড়ার উপায় ছিল না this
অনি

1
@ আইভায়লো স্লাভভ যদি উদাহরণ পদ্ধতি স্থির ছিল, তবে ক্ষেত্রটি স্থির থাকতে হবে, আমি চেষ্টা করেছিলাম - এবং কোনও 'এই পয়েন্টার' থাকবে না।
নিক্লাস

@ নিক্লাস, আপনাকে ধন্যবাদ উপসংহারে আমি মনে করি ল্যাম্বডাস তৈরির জন্য স্থির পদ্ধতিগুলি এই অপ্রয়োজনীয় পয়েন্টারের অভাবের নিশ্চয়তা দেবে।
Ivaylo Slavov

@ আইভায়লো স্লাভভ, অনুমান করুন .. :)
নিক্লাস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.