다음을 통해 공유


디자이너 serialization 개요

디자이너 serialization을 사용하면 디자인 타임 또는 런타임에 구성 요소의 상태를 유지할 수 있습니다.

개체 Serialization

.NET Framework는 코드 생성, SOAP serialization, 이진 serialization 및 XML serialization과 같은 몇 가지 serialization 형식을 지원합니다.

디자이너 serialization은 일반적으로 개발 도구와 연결된 개체 지속성의 종류를 포함하는 특별한 serialization 형식입니다. 디자이너 serialization은 개체 그래프를 나중에 개체 그래프를 복구하는 데 사용할 수 있도록 소스 파일로 변환하는 프로세스입니다. 소스 파일은 코드, 태그 또는 SQL 테이블 정보까지 포함할 수 있습니다. 디자이너 serialization은 모든 공용 언어 런타임 개체에 대해 사용할 수 있습니다.

디자이너 serialization은 다음과 같은 여러 가지 측면에서 일반적인 개체 serialization과 다릅니다.

  • serialization을 수행하는 개체는 런타임 개체와 별개이므로 디자인 타임 논리를 구성 요소에서 제거할 수 있습니다.

  • 개체가 완전히 초기화된 상태로 만들어진 다음 deserialization 중 속성 및 메서드 호출을 통해 수정된다는 전제 하에 serialization 체계가 만들어졌습니다.

  • 해당 개체에 대해 설정되지 않은 값이 있는 개체 속성은 serialize되지 않습니다. 반대로 deserialization 스트림은 모든 속성 값을 초기화하지 않을 수도 있습니다. serialization 규칙에 대한 자세한 내용은 이 항목에서 나중에 나오는 "일반적인 Serialization 규칙" 단원을 참조하십시오.

  • 개체의 완전한 serialization보다는 serialization 스트림 내부의 콘텐츠 품질이 더 중요시됩니다. 개체를 serialize할 방법이 정의되지 않으면 예외가 발생하지 않고 그 개체가 무시됩니다. 디자이너 serialization에서는 불투명 BLOB보다는 사람이 인식할 수 있는 간단한 형식으로 개체를 serialize할 수 있습니다.

  • serialization 스트림은 deserialization에 필요한 것보다 많은 데이터를 포함할 수 있습니다. 예를 들어, 소스 코드 serialization에는 사용자 코드가 개체 그래프를 deserialize하는 데 필요한 코드와 섞여 있습니다. 이 사용자 코드는 serialization에서는 유지되고 deserialization에서는 무시되어야 합니다.

참고

디자이너 serialization은 디자인 타임뿐만 아니라 런타임에서도 사용할 수 있습니다.

다음 표에서는 .NET Framework 디자이너 serialization 인프라로 달성할 수 있는 디자인 목표를 보여 줍니다.

디자인 목표

설명

모듈식

serialization 프로세스는 새 데이터 형식에 맞도록 확장 가능합니다. 이러한 데이터 형식은 자신에 대해 사람이 인식할 수 있는 유용한 설명을 제공할 수 있습니다.

쉽게 확장 가능

serialization 프로세스는 새로운 데이터 형식에 맞도록 쉽게 확장할 수 있습니다.

형식 중립적

개체는 각기 다른 여러 파일 형식에 참여할 수 있으며, 디자이너 serialization은 특정 데이터 형식에 얽매이지 않습니다.

아키텍처

디자이너 serialization 아키텍처는 메타데이터, serializer 및 serialization 관리자를 기반으로 합니다. 다음 표에서는 각 아키텍처 측면의 역할에 대해 설명합니다.

측면

설명

메타데이터 특성

특성은 형식 T를 serializer S와 관련시키는 데 사용됩니다. 또한 이 아키텍처는 serializer가 없는 형식에 대해 serializer를 제공할 수 있는 개체 설치에 사용 가능한 "부트스트래핑" 특성을 지원합니다.

Serializer

serializer는 특정 형식 또는 형식 범위를 serialize할 수 있는 개체입니다. 각 데이터 형식마다 기본 클래스가 있습니다. 예를 들어, 개체를 XML로 변환할 수 있는 DemoXmlSerializer 기본 클래스가 있을 수 있습니다. 이 아키텍처는 어떤 serialization 형식에 대해서도 독립적이며, CodeDOM(코드 문서 개체 모델)을 기반으로 한 이 아키텍처의 구현도 포함합니다.

Serialization 관리자

Serialization 관리자는 개체 그래프를 serialize하는 데 사용되는 모든 serializer에 대해 정보 저장소를 제공합니다. 50개 개체로 구성된 그래프는 모두 자체 출력을 생성하는 서로 다른 50가지의 serializer를 포함할 수 있습니다. 이 serializer는 serialization 관리자를 사용하여 서로 통신합니다.

다음 그림과 절차에서는 그래프의 개체(이 경우 A와 B)가 어떻게 serialize될 수 있는지 보여 줍니다.

개체 Serialize 그래프

그래프의 개체 serialization

  1. 호출자가 serialization 관리자에서 개체 A에 대한 serializer를 요청합니다.

    MySerializer s = manager.GetSerializer(a);
    
  2. A의 형식에 대한 메타데이터 특성이 A를 요청된 형식의 serializer에 바인딩합니다. 그러면 호출자는 serializer에 A를 serialize하도록 요청합니다.

    Blob b = s.Serialize(manager, a);
    
  3. 개체 A의 serializer가 A를 serialize합니다. A를 serialize하는 동안 만나는 개체 각각에 대해 serialization 관리자에서 추가 serializer를 요청합니다.

    MySerializer s2 = manager.GetSerializer(b);
    Blob b2 = s2.Serialize(manager, b);
    
  4. serialization의 결과가 호출자에게 반환됩니다.

    Blob b = ...
    

일반적인 Serialization 규칙

일반적으로 구성 요소는 수많은 속성을 노출합니다. 예를 들어, Windows Forms Button 컨트롤은 BackColor, ForeColorBackgroundImage와 같은 속성을 갖습니다. 디자이너에서 폼에 Button 컨트롤을 배치하고 생성된 코드를 보면, 코드에 해당 속성의 하위 집합만 유지되는 것을 확인하게 됩니다. 일반적으로 이것은 명시적으로 값을 설정한 속성입니다.

Button 컨트롤과 연결된 CodeDomSerializer가 serialization 동작을 정의합니다. 다음 목록에서는 CodeDomSerializer가 속성 값을 serialize하기 위해 사용하는 규칙 몇 가지에 대해 설명합니다.

  • 속성에 DesignerSerializationVisibilityAttribute가 연결되었으면, serializer는 이를 사용하여 해당 속성이 serialize되는지 여부(예: Visible 또는 Hidden)와 serialize하는 방법(Content)을 확인합니다.

  • Visible 또는 Hidden 값은 해당 속성이 serialize되는지 여부를 지정합니다. Content 값은 해당 속성이 serialize되는 방법을 지정합니다.

  • DemoProperty라는 속성의 경우 해당 구성 요소가 ShouldSerializeDemoProperty라는 메서드를 구현하면 디자이너 환경에서는 이 메서드에 대해 런타임 바인딩 호출을 수행하여 serialize할지의 여부를 확인합니다. 예를 들어, BackColor 속성의 경우 메서드를 사용하여 ShouldSerializeBackColor가 호출됩니다.

  • 속성에 지정된 DefaultValueAttribute가 있으면 기본값과 구성 요소의 현재 값이 비교됩니다. 현재 값이 기본값이 아닌 경우에만 속성이 serialize됩니다.

  • 구성 요소와 연결된 디자이너는 숨김 속성 또는 ShouldSerialize 메서드 자체 구현을 통해 serialization 결정에 참여할 수도 있습니다.

참고

serializer는 serialization 결정을 해당 속성과 연결된 PropertyDescriptor에게 맡기며, PropertyDescriptor는 이전의 규칙 목록을 사용합니다.

다른 방식으로 구성 요소를 serialize하려면 CodeDomSerializer에서 파생된 serializer 클래스를 직접 작성하고 DesignerSerializerAttribute를 사용하여 해당 구성 요소와 연결할 수 있습니다.

스마트 Serializer 구현

serializer 디자인의 요구 사항 중 하나는 새로운 serialization 형식이 필요할 경우 해당 형식을 지원하는 메타데이터 특성과 함께 모든 데이터 형식이 업데이트되어야 한다는 것입니다. 그러나 serialization 공급자와 일반 개체 메타데이터를 사용하는 serializer를 함께 사용하면 이 요구 사항을 해결할 수 있습니다. 이 단원에서는 여러 개의 사용자 지정 serializer를 사용할 필요성이 최소화되도록 특정 형식에 대해 serializer를 디자인하는 기본적인 방법을 설명합니다.

다음 스키마에서는 개체 그래프가 유지될 가상의 XML 형식을 정의합니다.

<TypeName>
    <PropertyName>
        ValueString
    </PropertyName>
</TypeName>

이 형식은 DemoXmlSerializer라는 개발된 클래스를 사용하여 serialize됩니다.

public abstract class DemoXmlSerializer 
{
    public abstract string Serialize(
        IDesignerSerializationManager m, 
        object graph);
}

DemoXmlSerializer는 단편으로 문자열을 구성하는 모듈식 클래스임을 이해해야 합니다. 예를 들어, Int32 데이터 형식의 DemoXmlSerializer는 값이 23인 정수가 전달되면 문자열 "23"을 반환합니다.

Serialization 공급자

이전 스키마 예제에서는 두 가지 기본 형식을 처리해야 함을 명확히 보여 줍니다.

  • 자식 속성이 있는 개체

  • 텍스트로 변환 가능한 개체

클래스를 텍스트나 XML 태그로 변환할 수 있는 사용자 지정 serializer로 모든 클래스를 표시하기는 어렵습니다. Serialization 공급자는 개체에 특정 형식에 대한 serializer를 제공할 기회가 부여되는 콜백 메커니즘을 제공하여 이 문제를 해결합니다. 이 예제에서는 사용 가능한 형식의 집합이 다음 조건에 의해 제한됩니다.

  • IConvertible 인터페이스를 사용하여 형식을 문자열로 변환할 수 있으면 StringXmlSerializer가 사용됩니다.

  • 형식이 문자열로 변환될 수 없지만 공용이고 빈 생성자가 있으면 ObjectXmlSerializer가 사용됩니다.

  • 두 조건 중 어느 것도 해당되지 않으면 serialization 공급자는 null을 반환해 해당 개체에 대한 serializer가 없음을 나타냅니다.

다음 코드 예제에서는 호출하는 serializer가 이 오류 상황을 해결하는 방법을 보여 줍니다.

internal class XmlSerializationProvider : IDesignerSerializationProvider 
{
    object GetSerializer(
        IDesignerSerializationManager manager, 
        object currentSerializer, 
        Type objectType, 
        Type serializerType) 
    {

        // Null values will be given a null type by this serializer.
        // This test handles this case.
        if (objectType == null) 
        {
            return StringXmlSerializer.Instance;
        }

        if (typeof(IConvertible).IsSubclassOf(objectType)) 
        {
            return StringXmlSerializer.Instance;
        }

        if (objectType.GetConstructor(new object[]) != null) 
        {
            return ObjectXmlSerializer.Instance;
        }

        return null;
    }
}

serialization 공급자가 정의되면 사용되어야 합니다. serialization 관리자는 AddSerializationProvider 메서드를 통해 serialization 공급자를 받을 수 있지만 serialization 관리자에 대해 호출이 이루어져야 합니다. DefaultSerializationProviderAttribute를 serializer에 추가하여 serialization 공급자를 serialization 관리자에 자동으로 추가할 수 있습니다. 이 특성에서는 serialization 공급자가 빈 공용 생성자를 가져야 합니다. 다음 코드 예제에서는 DemoXmlSerializer에 필요한 변경 사항을 보여 줍니다.

[DefaultSerializationProvider(typeof(XmlSerializationProvider))]
public abstract class DemoXmlSerializer 
{
}

이제 serialization 관리자가 모든 형식의 DemoXmlSerializer를 요청받으면, 기본 serialization 공급자가 serialization 관리자에 추가됩니다(아직 추가되지 않은 경우).

Serializer

샘플 DemoXmlSerializer 클래스는 StringXmlSerializer 및 ObjectXmlSerializer라는 이름의 구체적인 serializer 클래스를 갖습니다. 다음 코드 예제는 StringXmlSerializer의 구현을 보여 줍니다.

internal class StringXmlSerializer : DemoXmlSerializer
{
    internal StringXmlSerializer Instance = new StringXmlSerializer();

    public override string Serialize(
        IDesignerSerializationManager m, 
        object graph) 
    {

        if (graph == null) return string.Empty;

        IConvertible c = graph as IConvertible;
        if (c == null) 
        {
            // Rather than throwing exceptions, add a list of errors 
            // to the serialization manager.
            m.ReportError("Object is not IConvertible");
            return null;
        }

    return c.ToString(CultureInfo.InvariantCulture);
    }
}

ObjectXmlSerializer 구현이 개체의 공용 속성을 열거할 때 필요하므로 조금 더 복잡합니다. 다음 코드 예제서는 ObjectXmlSerializer의 구현을 보여 줍니다.

internal class ObjectXmlSerializer : DemoXmlSerializer 
{
    internal ObjectXmlSerializer Instance = new ObjectXmlSerializer();

    public override string Serialize(
        IDesignerSerializationManager m, 
        object graph) 
    {

        StringBuilder xml = new StringBuilder();
        xml.Append("<");
        xml.Append(graph.GetType().FullName);
        xml.Append(">");

        // Now, walk all the properties of the object.
        PropertyDescriptorCollection properties;
        Property p;

        properties = TypeDescriptor.GetProperties(graph);

        foreach(p in properties) 
        {
            if (!p.ShouldSerializeValue(graph)) 
            {
                continue;
            }

            object value = p.GetValue(graph);
            Type valueType = null;
            if (value != null) valueType = value.GetType();

            // Get the serializer for this property
            DemoXmlSerializer s = m.GetSerializer(
                valueType, 
                typeof(DemoXmlSerializer)) as DemoXmlSerializer;

            if (s == null) 
            {
                // Because there is no serializer, 
                // this property must be passed over.  
                // Tell the serialization manager
                // of the error.
                m.ReportError(string.Format(
                    "Property {0} does not support XML serialization",
                    p.Name));
                continue;
            }

            // You have a valid property to write.
            xml.Append("<");
            xml.Append(p.Name);
            xml.Append(">");

            xml.Append(s.Serialize(m, value);

            xml.Append("</");
            xml.Append(p.Name);
            xml.Append(">");
        }

        xml.Append("</");
        xml.Append(graph.GetType().FullName);
        xml.Append(">");
        return xml.ToString();
    }
}

ObjectXmlSerializer는 속성 값 각각에 대해 다른 serializer를 호출합니다. 여기에 두 가지 장점이 있습니다. 우선 ObjectXmlSerializer가 매우 간단해질 수 있습니다. 두 번째로는 타사 형식에 대해 확장성 지점을 제공합니다. 이 serializer 중 어느 방법으로도 작성할 수 없는 형식이 ObjectXmlSerializer에 있으면, 그 형식에 대해서는 사용자 지정 serializer를 제공할 수 있습니다.

용도

이렇게 새 serializer를 사용하려면 IDesignerSerializationManager의 인스턴스를 만들어야 합니다. 이 인스턴스에서 serializer를 요청한 다음 serializer에 개체를 serialize하도록 요청합니다. 다음 코드 예제에서는 Rectangle을 사용하여 개체를 serialize하는데, 이 형식에 빈 생성자와 IConvertible을 지원하는 네 가지 속성이 있기 때문입니다. IDesignerSerializationManager를 직접 구현하는 대신 DesignerSerializationManager에서 제공하는 구현을 사용할 수 있습니다.

Rectangle r = new Rectangle(5, 10, 15, 20);
DesignerSerializationManager m = new DesignerSerializationManager();
DemoXmlSerializer x = (DemoXmlSerializer)m.GetSerializer(
    r.GetType(), typeof(DemoXmlSerializer);

string xml = x.Serialize(m, r);

이렇게 하면 다음과 같은 XML이 만들어집니다.

<System.Drawing.Rectangle>
<X>
5
</X>
<Y>
10
</Y>
<Width>
15
</Width>
<Height>
15
</Height>
</System.Drawing.Rectangle>

참고

이 XML은 들여쓰여져 있지 않습니다. 이는 IDesignerSerializationManager에서 Context 속성을 통해 손쉽게 수행할 수 있습니다. 각 serializer 수준에서 현재 들여쓰기 수준이 포함된 컨텍스트 스택에 개체를 추가하고, 각 serializer는 스택에서 해당 개체를 검색하고 이를 사용하여 들여쓰기를 제공할 수 있습니다.

참고 항목

참조

IDesignerSerializationManager

DesignerSerializationManager

DefaultSerializationProviderAttribute

IConvertible

CodeDomSerializerBase

기타 리소스

디자인 타임 지원 확장