none
Как получить полную копию объекта ? RRS feed

  • Вопрос

  • Есть вот такой вот класс:

     

    public class OptionCombination : INotifyPropertyChanged
        {
            public Portfolio _Portfolio { get; set; }
            public BindingList<ExtendedPosition> ComboElements { get; set; }
            public HedgingMethods HedgeMethod { get; set; }
            public Greeks SumGreeks { get; set; }
            public bool IsHedging { get; set; }
            public HedgeParameters hedgeParameters { get; set; }
         }
    
    Мне его надо изменять, но хочется, чтобы изменения вносились не сразу, а при нажатии кнопки. Поэтому нужно создавать копию, т.к. при простом копировании я получаю только ссылку на объект. При попытке клонирования методом MemberwiseClone создается неглубокая , почти пустая копия.  Есть ли универсальный какой-то метод для получения копии объекта? А то у меня много надо клонировать :)
    20 января 2012 г. 6:37

Ответы

  • > Как можно пройтись по всему объекту, найти везде вхождения этого поля ну и заполнить его значением?
     
      
    с помощью методов Type и TypeDescriptor. пример здесь (метод ReadAll).
    если в структуре данных есть цикличные ссылки (объекты ссылаются друг на друга)
    то см. ObjectIDGenerator Class
      
      
     
    • Помечено в качестве ответа Dottz 3 февраля 2012 г. 6:27
    28 января 2012 г. 20:10

Все ответы

  • Можно использовать сериализацию - How do you do a deep copy an object in .Net (C# specifically)?, наверное это наиболее простой метод, чтобы не создавать руками копии всех полей класса.


    Для связи [mail]
    20 января 2012 г. 7:28
  • > Есть ли универсальный какой-то метод для получения копии объекта?
     
     
    можно использовать сериализацию. но есть ограничения.

    в классе есть список: BindingList<ExtendedPosition> ComboElements -- его элементы тоже надо клонировать?
     
     
    20 января 2012 г. 7:30
  • Да, а в ExtendedPosition тоже есть список. Поэтому и сложности...
    20 января 2012 г. 7:36
  • Если вы найдете более универсальное решение, то обязательно поделитесь, а пока лично я добавляю в такие классы функцию GetCopy которая бытовым копированием всех полей создает копию класса.
    Влюблен в WPF Не пишу на C#
    20 января 2012 г. 7:41
    Отвечающий
  • > Есть вот такой вот класс [...] Мне его надо изменять, но хочется, чтобы изменения вносились не сразу, а при нажатии кнопки. Поэтому нужно создавать копию


    объект привязан к UI. изменения вносятся через контролы.
    если на UI нажать на конпку Сancel, то надо отменить изменения. такой сценарий?
     
     
    20 января 2012 г. 7:48
  • Да, объект привязан к UI для отображения и изменения, но необходимо вносить изменения в объект не сразу после изменения значения в контролах, а после нажатия кнопки "Принять изменения". Про Cancel я и не думал, это не так важно. Т.е. изменения вносятся не сразу в объект, а в его копию, а потом он заменяется копией.
    20 января 2012 г. 7:56
  • Если цель в этом, то попробуйте так, как описано в этой статье. То есть всем привязкам назначьте UpdateSourceTrigger в  Explict и внесенные изменение будут переданы источнику только после вызова метода UpdateSource а его можно вызвать в событии кнопки "Принять изменения"
    Влюблен в WPF Не пишу на C#
    • Предложено в качестве ответа PashaPash 20 января 2012 г. 10:33
    20 января 2012 г. 9:27
    Отвечающий
  • Попробовал сериализацию использовать, но и тут не без косяков: данный метод работает для классов, помеченных атрибутом [Serializable], а я еще использую закрытую библиотеку и ее встроенные классы, соответственно им я не могу указать этот атрибут.

    Стал пробовать сериализацию в XML , там вроде пофиг, сериализуемый ли класс или нет, надо лишь тип указать и чтобы был в наличии конструктор без параметров. Этот способ вроде почти подошел, но не тут-то было : не сериализуется одно поле с очень сложной структурой (почему - не знаю, там вообще темный лес). Вот я и думаю, может мне после десериализации в копии это свойство в ручную прописывать? Только вот тут дело в том, что свойство это в моем объекте много где встречается в других классах. Как можно пройтись по всему объекту, найти везде вхождения этого поля ну и заполнить его значением?

    20 января 2012 г. 11:24
  • всем привязкам назначьте UpdateSourceTrigger в  Explict и внесенные изменение будут переданы источнику только после вызова метода UpdateSource а его можно вызвать в событии кнопки "Принять изменения"

    Я вывожу в Listview, у которого каждый столбец отображается при помощи DataTemplate, путем указания CellTemplate.

    <GridViewColumn CellTemplate="{StaticResource ConditionsCombo}" Header="Условие" />
    
    //-------------------------------------------------------
    
    <DataTemplate x:Key="ConditionsCombo">
                <StackPanel Orientation="Horizontal">
                    <ComboBox ItemsSource="{Binding Source={StaticResource Conditions}}" SelectedItem="{Binding Path=condition}"/>
                </StackPanel>
    </DataTemplate>
    

    DataTemplate используется для вывода значения не в обычный TextBlock, а в ComboBox, у которого Binding идет к SelectedItem. Обратиться к столбцу , который использует DataTemplate и обновить привязку я не могу, ибо у GridViewColumn нет метода GetBindingExpression. Как еще можно сделать? Вариант этот вроде менее геморройный, чем с копированием.

    20 января 2012 г. 13:04
  • DataTemplate нужен для получения желаемого отображения, но как я понимаю главная привязка это привязка к ListView и как раз ее вам нужно обновлять по требованию.


    Влюблен в WPF Не пишу на C#
    20 января 2012 г. 15:31
    Отвечающий
  • Я попробовал обновлять Binding у Listview, но только метод GetBindingExpression(ListView.ItemsSourceProperty) возвращает null. Соответственно, обновить привязку не получается. ItemsSource я указываю не в XAML, а в коде, ибо иногда меняются привязки.
    • Изменено Dottz 23 января 2012 г. 7:17
    23 января 2012 г. 7:14
  • Ну если вы покажете этот кусок кода будет легче понять что да как.
    Влюблен в WPF Не пишу на C#
    23 января 2012 г. 7:25
    Отвечающий
  • XAML:

    <Window.Resources>
            <DataTemplate x:Key="CheckCell">
                <StackPanel Orientation="Horizontal">
                    <CheckBox IsChecked="{Binding Path=IsSelected, 
              RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}" Padding="0" Margin="0" />
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="EditablePrice">
                <StackPanel Orientation="Horizontal">
                    <TextBox Name="PriceBox" Padding="0" Margin="0" Text="{Binding Path=price,UpdateSourceTrigger=Explicit}" Width="70" Height="24"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="EditableStep">
                <StackPanel Orientation="Horizontal">
                    <TextBox Padding="0" Margin="0" Text="{Binding Path=step,UpdateSourceTrigger=Explicit}" Width="70" Height="24"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="EditableDelta">
                <StackPanel Orientation="Horizontal">
                    <TextBox Padding="0" Margin="0" Text="{Binding Path=delta, UpdateSourceTrigger=Explicit}" Width="70" Height="24"/>
                </StackPanel>
            </DataTemplate>
            <ObjectDataProvider x:Key="Conditions" MethodName="GetValues" ObjectType="{x:Type sys:Enum}" >
                <ObjectDataProvider.MethodParameters>
                    <x:Type TypeName="data:HedgingConditions"></x:Type>
                </ObjectDataProvider.MethodParameters>
            </ObjectDataProvider>
            <ObjectDataProvider x:Key="StepTypes" MethodName="GetValues" ObjectType="{x:Type sys:Enum}" >
                <ObjectDataProvider.MethodParameters>
                    <x:Type TypeName="data:HedgingSteps"></x:Type>
                </ObjectDataProvider.MethodParameters>
            </ObjectDataProvider>
            <DataTemplate x:Key="ConditionsCombo">
                <StackPanel Orientation="Horizontal">
                    <ComboBox ItemsSource="{Binding Source={StaticResource Conditions},UpdateSourceTrigger=Explicit}" SelectedItem="{Binding Path=condition}"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate x:Key="StepsCombo">
                <StackPanel Orientation="Horizontal">
                    <ComboBox ItemsSource="{Binding Source={StaticResource StepTypes},UpdateSourceTrigger=Explicit}" SelectedItem="{Binding Path=stepType}"/>
                </StackPanel>
            </DataTemplate>
        </Window.Resources>
    
    <ListView Name="PriceLevelsView" Height="81" HorizontalAlignment="Left" Margin="6,67,0,0" VerticalAlignment="Top" Width="345" >
                        <ListView.View>
                            <GridView>
                                <GridViewColumn CellTemplate="{StaticResource CheckCell}" Width="25" />
                                <GridViewColumn CellTemplate="{StaticResource ConditionsCombo}" Header="Условие" Width="55" />
                                <GridViewColumn CellTemplate="{StaticResource EditablePrice}" Header="Цена" Width="80" />
                                <GridViewColumn CellTemplate="{StaticResource StepsCombo}" Header="Тип шага" Width="60" />
                                <GridViewColumn CellTemplate="{StaticResource EditableStep}" Header="Шаг" Width="40" />
                                <GridViewColumn CellTemplate="{StaticResource EditableDelta}" x:Name="asad"  Header="Дельта" Width="60" />
                            </GridView>
                        </ListView.View>
    </ListView>
    

    C#


    public MainWindow()
            {
                InitializeComponent();
                
                PriceLevel pl = new PriceLevel(50000, HedgingConditions.Less, HedgingSteps.Delta, 50, 2);
                PriceLevel pl2 = new PriceLevel(30000, HedgingConditions.More, HedgingSteps.Base, 50, 2);
                var PriceLevels = new BindingList<PriceLevel>();
                PriceLevels.Add(pl);
                PriceLevels.Add(pl2);
                
                this.PriceLevelsView.ItemsSource = this.PriceLevels;
                
            }
    private void Accept_Click(object sender, RoutedEventArgs e) { var be = PriceLevelsView.GetBindingExpression(ListView.ItemsSourceProperty); be.UpdateSource(); }  
    • Изменено Dottz 23 января 2012 г. 7:43
    23 января 2012 г. 7:38
  • Вот эта строка

     this.PriceLevelsView.ItemsSource = this.PriceLevels;

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


    Влюблен в WPF Не пишу на C#
    24 января 2012 г. 10:59
    Отвечающий
  • Заменил 

    this.PriceLevelsView.ItemsSource = this.PriceLevels;

    на

    Binding b = new Binding();
    b.Source = PriceLevels;
    b.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
    PriceLevelsView.SetBinding(ListView.ItemsSourceProperty, b);
    

    Работает точно так же - обновляет сразу при внесении изменений в контролах.

    upd: Забыл указать , что PriceLevels объявлен как свойство:

    public BindingList<PriceLevel> PriceLevels { get; set; }

    • Изменено Dottz 24 января 2012 г. 12:14
    24 января 2012 г. 12:02
  • > Как можно пройтись по всему объекту, найти везде вхождения этого поля ну и заполнить его значением?
     
      
    с помощью методов Type и TypeDescriptor. пример здесь (метод ReadAll).
    если в структуре данных есть цикличные ссылки (объекты ссылаются друг на друга)
    то см. ObjectIDGenerator Class
      
      
     
    • Помечено в качестве ответа Dottz 3 февраля 2012 г. 6:27
    28 января 2012 г. 20:10
  • Уважаемый пользователь!

    В вашей теме отсутствует активность в течение последних 5 дней. При отсутствии каких-либо действий в течение 2 последующих дней, тема будет переведена в разряд обсуждений. Вы можете возобновить дискуссию, просто оставив сообщение в данной теме


    Для связи [mail]
    2 февраля 2012 г. 10:53