0

ComboBox 選択のイベント ハンドラーでディープ コピーを実行する必要があります 選択が変更されまし
た DeepCopy は失敗します
しかし、まったく同じ DeepCopy が ctor とボタン クリックのイベント ハンドラーで成功します
2 台のマシンでこれを再現しまし
た ハンドラーまたはディープ コピーを変更したいと思っていますが、私はComboBox SelectionChanged のイベント ハンドラーでかなり必要です。

コードでこれが失敗するのを探します

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel;

namespace DeepCopy
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private List<TestClass> testClassList = new List<TestClass>();
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            try
            {
                TestClass tca = new TestClass();
                tca.Prop1 = "Tcaa";
                TestClass tcb = DeepClone<TestClass>(tca);
                tca.Prop1 = "TcaNew1";
                System.Diagnostics.Debug.WriteLine(tca.Prop1);
                System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                tcb.Prop1 = "sdf1";
                System.Diagnostics.Debug.WriteLine(tcb.Prop1);
            }
            catch (Exception Ex)
            {
                System.Diagnostics.Debug.WriteLine(Ex.Message);
            }
            TestClass tc = new TestClass();
            tc.Prop1 = "tc1";
            testClassList.Add(tc);
            tc = new TestClass();
            tc.Prop1 = "tc2";
            testClassList.Add(tc);
            foreach (TestClass tcx in TestClassList)
            {
                try
                {
                    // this does not fail
                    TestClass tcb = DeepClone<TestClass>(tcx);
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tc.Prop1 += "ctorA";
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tcb.Prop1 += "ctorB";
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }
        public List<TestClass> TestClassList { get { return testClassList; } }
        [Serializable()]
        public abstract class TestAstract : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            protected void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
            public abstract string Prop1 { get; set; }
        }
        [Serializable()]
        public class TestClass : TestAstract
        {            
            private string prop1;
            public override string Prop1 
            {
                get { return prop1; }
                set
                {
                    if (prop1 == value) return;
                    prop1 = value;
                    NotifyPropertyChanged("Prop1");
                }
            }
            public TestClass() { }
        }
        public static T DeepClone<T>(T obj)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;

                return (T)formatter.Deserialize(ms);
            }
        }

        private void clickDeepCopy(object sender, RoutedEventArgs e)
        {
            foreach (TestClass tc in TestClassList)
            {
                try
                {
                    // this does not fail
                    TestClass tcb = DeepClone<TestClass>(tc);
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tc.Prop1 += "clickA";
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tcb.Prop1 += "clickB";
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }

        private void cbSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (TestClass tc in TestClassList)
            {
                try
                {
                    // this fails
                    TestClass tcb = DeepClone<TestClass>(tc);
                    // A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
                    // Type 'System.ComponentModel.PropertyChangedEventManager' in Assembly 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable.
                    // A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tc.Prop1 += "cbceA";
                    System.Diagnostics.Debug.WriteLine(tc.Prop1);
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                    tcb.Prop1 = "cbceB";
                    System.Diagnostics.Debug.WriteLine(tcb.Prop1);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }
    }
}

<Window x:Class="DeepCopy.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="DeepCopy" Click="clickDeepCopy" />
        <ComboBox Grid.Row="1" ItemsSource="{Binding Path=TestClassList}" DisplayMemberPath="Prop1" SelectionChanged="cbSelectionChanged" />
    </Grid>
</Window>

もっと興味深い
空の ComboBox SelectionChanged イベント ハンドラーを試してみました 空
の ComboBox SelectionChanged が呼び出された後、ボタン クリックは失敗します

XAML で ComboBox SelectionChanged イベント ハンドラーを削除しました
ボタン クリックは ComboBox SelectionChanged の後で失敗します - イベント ハンドラーがなくても
、ボタン クリックは ComboBox SelectionChanged の前に成功します

XAML で ComboBox SelectionChanged イベント ハンドラーを削除しました XAML
で SelectedIndex = 0 を設定し
ます ボタン クリック イベント ハンドラーは、最初は失敗します

SelectionChanged イベント ハンドラーがなく、ComboBox に SelectedIndex がない場合でも、Get は TestClassList で呼び出されます
。 raw バインディングだけではディープ コピーは壊れません。ComboBox で
アイテムが選択されると、ディープ コピーが壊れます。

私はあなたが疑わしいことを知っていますが、私はこれに数時間を費やしました

4

2 に答える 2

3

イベントに [field:NonSerialized] を追加する必要があります

[field:NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
于 2013-11-11T04:31:12.133 に答える
2

PropertyChanged イベントのシリアル化を無効にする必要があります (つまり、サブスクライバーはデータ バインディングのために ComboBox を好みます)。

この同様の問題を見てみましょうオブジェクトをシリアル化するときにイベント サブスクライバーを無視するにはどうすればよいですか?

于 2013-11-11T03:08:37.780 に答える