Сообщений 0    Оценка 48        Оценить  
Система Orphus

.NET Q&A

Авторы: VladD2
AndrewVK

Источник: RSDN Magazine #5-2004
Опубликовано: 27.12.2002
Исправлено: 10.12.2016
Версия текста: 1.0
Q. Как проще получить точную копию произвольного объекта?
Q. Как сделать программно скриншот формы?

Q. Как проще получить точную копию произвольного объекта?

А. Это сложный вопрос. Все зависит от того, насколько копия должна быть копией. :)

Если речь идет о поверхностной копии (то есть когда копируется только содержимое объекта, но все ссылки, содержащиеся в объекте, остаются теми же), то достаточно в своем классе реализовать метод Clone, в котором вызвать защищенный метод MemberwiseClone(). Однако тут есть две проблемы:

1. Может понадобиться глубокая копия.

2. Скорость MemberwiseClone().

ПРИМЕЧАНИЕ

Структуры копируются присвоением, так что тут речь идет о ссылочных объектах.

Обе проблемы можно решить написанием метода клонирования вручную (например, рекурсивно вызывая написанные вручную методы клонирования, которые производят почленное клонирование). Но это довольно трудоемко. Если классов 1-5, это еще ничего, но если их сотни...

Поверхностное клонирование можно также реализовать, если все члены данных описать в структуре, и включать в состав класса эту структуру, а не отдельные члены:

        struct Data
{
  publicint _i;
  publicstring _s;
}

class A
{
  public A() { }

  public A(int i, string s)
  {
    _data._i = i;
    _data._s = s;
  }

  private Data _data;

  publicint    iProp { get { return _data._i; } set { _data._i = value; } }
  publicstring sProp { get { return _data._s; } set { _data._s = value; } }

  public A Clone()
  {
    A a = new A();
    // Код клонирования
    [b]a._data = this._data;[/b]
    return a;
  }

  publicoverridestring ToString()
  {
    return"iProp = " + iProp + "; sProp = " + sProp + ";";
  }
}

class Program
{
  staticvoid Main(string[] args)
  {
    A a1 = new A(1, "test");
    Console.WriteLine("a1 = " + a1);
    A a2 = a1.Clone();
    Console.WriteLine("a2 = " + a2);
  }
}

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

Если же нужно клонировать все объекты, на которые ссылается клонируемый, т.е. производить глубокое клонирование, то нужно или вызвать клонирование рекурсивно для всех не примитивных подобъектов, или производить сериализацию и десериализацию объекта. Первый случай требует большого объема кодирования, второй требует, чтобы все объекты были сериализуемы, и значительно (скорее всего, на пару порядков) медленнее (сериализация в .NET далека от идеала).

В общем, если код клонирования нужно создать для большого количества классов, все довольно грустно. Однако есть одно универсальное решение. Код клонирования можно генерировать автоматически на базе информации о типах объекта. Путей тут три:

  1. Описывать объекты в некой удобной для распознавания форме и генерировать описание по ней. Например, можно описывать объект в виде XML-файлов, а потом генерировать код с помощью XSLT-шаблона. Этот способ описан в статье Андрея Корявченко Алгоритмы кодогенерации.
  2. Можно создать приложение, которое будет генерировать код, читая исходную информацию через механизм отражения. При этом удобно вставлять код по месту, помечая его директивой #region. Если задать уникальный текст у #region, и такой же у #endregion, то несложно создать код, позволяющий заменить текст внутри этого региона. Таким образом, приложение формирует код метода клонирования и заменяет им старый код, помеченный директивой "#region Clone". Пример подобной реализации и исходный код базового класса, упрощающего создание подобных генераторов, можно взять в проекте R#. Хотя скорее всего, в ближайшее время в нем будет использоваться способ 3. :) Облегчить генерацию кода по информации о типах можно, используя инструменты наподобие CodeSmith.
  3. Использовать для генерации кода компилятор R#. Это специальное средство, предназначенное для решения подобных задач. Оно пока что еще не закончено, но подобные задачи можно решать уже сейчас. В ближайшее время мы постараемся перевести всю генерацию кода для проекта R# на него самого.

Q. Как сделать программно скриншот формы?

A. Вот очень простое решение.

        using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class TestForm : Form
{
    staticvoid Main()
    {
        Application.Run(new TestForm());
    }

    private TestForm()
    {
        Text = "TestForm";

        Button pb = new Button();
        pb.Text = "Print";
        pb.Location = new Point(10, 10);
        pb.Click += new EventHandler(pb_Click);
        Controls.Add(pb);

        Button cb = new Button();
        cb.Text = "Close";
        cb.Location = new Point(120, 10);
        cb.Click += new EventHandler(cb_Click);
        Controls.Add(cb);

        PropertyGrid pg = new PropertyGrid();
        pg.SelectedObject = pb;
        pg.Location = new Point(10, 50);
        Controls.Add(pg);
    }

    privatevoid cb_Click(object sender, EventArgs e)
    {
        Close();
    }

    [Flags]
    privateenum DrawingOptions
    {
        PRF_CHECKVISIBLE = 0x00000001,
        PRF_NONCLIENT = 0x00000002,
        PRF_CLIENT = 0x00000004,
        PRF_ERASEBKGND = 0x00000008,
        PRF_CHILDREN = 0x00000010,
        PRF_OWNED = 0x00000020
    }

    privateconstint WM_PRINT = 0x0317;
    privateconstint WM_PRINTCLIENT = 0x0318;

    [DllImport("user32.dll")]
    privatestaticexternint SendMessage(IntPtr hWnd, int msg, IntPtr dc,
        DrawingOptions opts);

    privatevoid pb_Click(object sender, EventArgs e)
    {
        using (Bitmap bm = new Bitmap(Width + 2, Height + 2))
        using (Graphics g = Graphics.FromImage(bm))
        {
            IntPtr dc = g.GetHdc();
            try
            {
                SendMessage(Handle, WM_PRINT, dc,
                    DrawingOptions.PRF_CHILDREN |
                    DrawingOptions.PRF_CLIENT |
                    DrawingOptions.PRF_NONCLIENT);
            }
            finally
            {
                g.ReleaseHdc(dc);
            }
            bm.Save("bm.png");
        }
    }
}


Эта статья опубликована в журнале RSDN Magazine #5-2004. Информацию о журнале можно найти здесь
    Сообщений 0    Оценка 48        Оценить