5

次のコードがあるとします。

public class Foo
{
    private int x;
    private int y;

    public Bar CreateBar()
    {
        return new Bar(x, () => y);
    }
}

[Serializable]
public class Bar
{
    private int a;
    private Func<int> b;

    public Bar(int a, Func<int> b)
    {
        this.a = a;
        this.b = b;
    }
}

このシナリオでは、オブジェクトと値のスコープはどうなりますか? x は値型であるため、値によって Bar に渡されます。したがって、そのスコープに何もする必要はありません。しかし、yはどうなりますか?y の値は、b が実際に評価されたときに返されるようにしておく必要があります。Foo はすべて、後で y を評価するために保持されていますか? Foo は GC されていないとしか思えません。

ここで、Bar をディスクにシリアライズし、後でデシリアライズするとします。実際に連載されたのは?Fooも連載したの?Bar がデシリアライズされた後に b を評価できるようにするには、どのような魔法が使われているのでしょうか? IL で何が起こっているのか説明できますか?

4

4 に答える 4

5

更新: IL に頼らずに実際に何が起こっているかを確認するには:リフレクターを使用して匿名メソッドとキャプチャされた変数を理解する


使用する場合:

public Bar CreateBar()
{
    return new Bar(x, () => y);
}

あなたは暗黙のうちにthis.y;を意味しています。したがって、デリゲートに関しては、含まれているのはへの参照Fooです。そのため、Bar(デリゲートを介して)のインスタンスは、 がコレクションに使用可能にFooなるまで、(ガベージBarコレクションではなく) 全体を維持します。

特に、キャプチャされた変数を処理するためにコンパイラが追加のクラスを生成する必要はありません (この場合)。必要なのはインスタンスだけなFooので、 でメソッドを生成できますFoo。デリゲートがローカル変数 ( 以外) を含む場合、これはより複雑になりますthis

シリアライゼーションに関しては...そうですね、デリゲートをシリアライズすることは非常に悪い考えです。ただし、BinaryFormatter デリゲートをウォークし、(理論的には) serialized Bar、 serialized Foo、および serialized デリゲートでそれらをリンクすることができますが、 としてマークした場合のみです。Foo[Serializable]

しかし、私は強調します - これは悪い考えです。BinaryFormatter私は(さまざまな理由で)めったに使用しませんが、それを使用している人々からよく寄せられる質問は、「なぜシリアライズしようとしているのか(ランダムなタイプ)」です。通常、答えは「イベントを公開していて、サブスクライバーをシリアル化しようとしています」です。この場合、最も一般的な修正は、イベントのフィールドを としてマークすること[NonSerialized]です。


ILを見るのではなく。これを調査する別の方法は、リフレクターを .NET 1.0 モードで使用することです (つまり、匿名メソッドでスワップせずに)。その後、次のことがわかります。

public Bar CreateBar()
{
    return new Bar(this.x, new Func<int>(this.<CreateBar>b__0));
}
[CompilerGenerated]
private int <CreateBar>b__0()
{
    return this.y;
}

ご覧のように; に渡されるのは、現在のインスタンス ( )Barの隠しメソッド ( と呼ばれる) へのデリゲートです。したがって、に渡されるのはcurrentのインスタンスです。<CreateBar>b__0()thisFooBar

于 2009-08-15T08:08:12.507 に答える
1

シリアル化するオブジェクトを反映しているときにシリアル化しようとするとエラーが発生しました。

私の例:

[Serializable]
    public class SerializeTest
    {
        //public SerializeTest(int a, Func<int> b)
        //{
        //    this.a = a;
        //    this.b = b;
        //}

        public SerializeTest()
        {

        }

        public int A 
        {
            get
            {
                return a;
            }

            set
            {
                a = value;
            }
        }
        public Func<int> B 
        {
            get
            {
                return b;
            }
            set
            {
                b = value;
            }
        }


        #region properties

        private int a;
        private Func<int> b;



        #endregion

        //serialize itself
        public string Serialize()
        {
            MemoryStream memoryStream = new MemoryStream();

            XmlSerializer xs = new XmlSerializer(typeof(SerializeTest));
            using (StreamWriter xmlTextWriter = new StreamWriter(memoryStream))
            {
                xs.Serialize(xmlTextWriter, this);
                xmlTextWriter.Flush();
                //xmlTextWriter.Close();
                memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
                memoryStream.Seek(0, SeekOrigin.Begin);
                StreamReader reader = new StreamReader(memoryStream);

                return reader.ReadToEnd();
            }
        }

        //deserialize into itself
        public void Deserialize(string xmlString)
        {
            String XmlizedString = null;

            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (StreamWriter w = new StreamWriter(memoryStream))
                {
                    w.Write(xmlString);
                    w.Flush();

                    XmlSerializer xs = new XmlSerializer(typeof(SerializeTest));
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    XmlReader reader = XmlReader.Create(memoryStream);

                    SerializeTest currentConfig = (SerializeTest)xs.Deserialize(reader);

                    this.a = currentConfig.a;
                    this.b = currentConfig.b;

                    w.Close();
                }
            }
        }

    }

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

            SerializeTest test = new SerializeTest() { A = 5, B = ()=>67};
            string serializedString =  test.Serialize();


}
}

これを行うためのリンクは次のとおりです...もう少し複雑です:AnonDelegatesのシリアル化

于 2009-08-14T17:40:09.463 に答える
0

クイックテストプロジェクトを作成して値を出力し、それらを確認します。それは質問に答えるはずであり、おそらくあなたがその過程で何か特別なことを学ぶようになるでしょう。(これはあなたの質問に答えるほとんどの人がしたことです。)

于 2009-08-14T17:36:17.803 に答える
0

Foo オブジェクトの x と y は値型としてキャプチャされると思います。したがって、そのラムダ式用に作成されたクロージャーは、Foo オブジェクトへの参照を保持するべきではありません。したがって、コンパイラはそのクロージャのクラスを次のように作成できます。

internal class CompilerGeneratedClassName
{
   private int x;
   private int y;
   public CompilerGeneratedClassName(int x, int y)
   {
     this.x = x;
     this.y = y;
   }

   public int CompilerGeneratedMethodName()
   {
     return this.y;
   }     
}

return new Bar(x, () => y); 

に置き換えることができます

return new Bar(x,new CompilerGeneratedClassName(x,y).CompilerGeneratedMethodName);

したがって、この閉鎖の結果として Foo オブジェクトへの参照があるとは思いません。したがって、Foo オブジェクトは GCed である可能性があります。私は間違っているかもしれません。できることの 1 つは、小さなプログラムを作成してコンパイルし、生成された IL を ILDASM ツールで検査することです。

于 2009-08-14T18:14:25.277 に答える