2008/C#2008. 7. 25. 08:55

1. 리플렉션의 의미와 그 필요성

리플렉션은 사전적 의미인 "어떤 사물을 비추어 보다"와 동일한 기능상 의미를 지닙니다. 즉, 리플렉션은 코드 구문 상에서는 사용할 수 없는 제 3의 형식 개체 (클래스, 대리자, 나열 상수, 구조체, 인터페이스 등)를 동적으로 다루는 기술을 의미합니다.

리플렉션은 그닥 활용하기 쉽지도 않을 뿐더러 활용할 만한 곳을 찾기도 어렵습니다. 따라서 사람들이 잘 뒤져보지 않는 기술이기도 합니다. 하지만 리플렉션을 빼면 닷넷은 이빨빠진 호랑이, 더 심한 표현을 쓰면 뼈대 없는 건물로 전락하고야 맙니다. 그만큼 리플렉션을 활용하는 곳이 많다는 것을 강조하고 싶습니다.

2. 쉬운 리플렉션부터 살펴봅시다!

리플렉션에 대해서는 차근차근히 수순을 밟아나가는 것이 이해에 도움이 됩니다. 가장 쉬운 리플렉션부터 공략해 봅시다.

사용하는 디자인 패턴에 따라서 상속을 받는 자식 클래스를 만드는 디자인 패턴을 사용하는 경우도 가끔있습니다. 이 경우 리플렉션의 가장 대표적인 예를 시험해 볼 수 있습니다. 한가지 실험을 해봅시다.

배열을 가지고 우리는 두 가지의 리플렉션을 시험해 볼 수 있겠습니다. 프레임워크 (주: CLS 규격을 구현하는 프레임워크의 수가 많기 때문에 특정 프레임워크를 지칭할 수는 없습니다. 따라서 Microsoft .NET, Rotor, Mono, DotGNU 등을 한꺼번에 아울러 프레임워크라고 표현하겠습니다.)는 언어 문법에서 사용하는 배열의 인스턴스를 System.Array 클래스를 기초해서 동적으로 생성합니다.

System.Array 클래스는 배열에 관한 기본적인 기능을 정의한 클래스이지만 System.Array 클래스의 생성자를 우리가 직접 호출할 수는 없습니다. 그리고 CLS 규격에서 명시하였던 바와 같이 System.Object가 System.Array에 대한 상위 클래스임은 변함이 없습니다. 이와 같은 전제 조건하에서 시험을 해보겠습니다.

2.1. System.Array의 활용

배열은 가지고 있는 원소 (Atom/Element)의 형식에 따라서 각각 다른 배열로 취급되며 배열 vs. 배열 단위의 형변환은 비록 두 배열의 원소의 형식 관계가 상속 관계라고 할지라도 기본적으로는 허용되지 않습니다. 하지만 "배열" 이라는 특성은 원소의 형식과는 관계없이 공통적인 것입니다. 우리는 각기 다른 원소 형식을 가지는 배열을 System.Array 라는 클래스로 동일하게 취급할 수 있습니다. 어떤 형태의 원소 형식을 가지는 배열이 되었던간에 상관없이 말입니다.

Array test = new string [5];
test.SetValue("Test", 2);
string[] test2 = (string[])test;
Console.WriteLine(test2[2]);

기본적으로 System.Array는 추상 클래스이므로 System.Array의 생성자는 사용할 수 없습니다. 하지만 어떻게 인스턴스가 만들어지게 된 것일까요? 명시되어 있지는 않지만 컴파일러 내부적으로는 또 하나의 클래스를 System.Array로부터 동적으로 파생시켰다고 가정하고 다루게 됩니다. 다시 말하여 test 라는 인스턴스는 파생된 클래스의 멤버들 가운데서 System.Array의 멤버들과 일치하는 멤버들만을 가르키게 됩니다.

Array 클래스에 값을 대입하기 위해서 SetValue 메서드를 호출합니다. Array 클래스에서는 인덱서 연산자를 구현하지 않으므로 원시 함수인 SetValue 메서드를 직접 호출하게 되었습니다. 이 상태에서 string[] 클래스로 형변환을 다시 시도합니다. 그리고 값을 확인해 봅니다. Test 라는 문자열이 그대로 유지된 것을 확인하실 수 있습니다.

위의 예에서 중요한 원리 하나를 확인할 수 있습니다. 첫 번째는 형변환에는 방향성이 존재한다는 것입니다. 형식의 변환이 추상화될 수록 (위로 갈수록) 가급적 공통 부분으로만 표현됩니다. 반대로 형식의 변환이 구체화될 수록 (아래로 갈수록) 가급적 상세하게 표현됩니다.

데브피아 남정현님

Posted by penguindori
2008/C#2008. 7. 25. 08:45

다음 예제는 클라이언트가 객체를 서버로 전송후  DB에 넣고,

다시 서버가 DB에서 객체를 읽어서 클라이언트로 보내는 예제입니다


DB에 객체가 삽입될 memo 컬럼은 longtext 타입입니다


CREATE TABLE memo (
  id VARCHAR(8),
  memo LONGTEXT
 );


DB는 mysql을 사용했습니다

DB마다 약간씩 차이가 있더군요

참고하십시오



------------- 서버 -------------

    class Program
    {
        static void Main(string[] args)
        {
            MyConvert mc = new MyConvert();

            Socket m_listenSocket;
            Socket m_clientSocket;
            IPEndPoint m_listenEP;

            MyClass myClass = new MyClass();
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();

            Console.WriteLine("서버 시작");

            m_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            m_listenEP = new IPEndPoint(IPAddress.Any, 555);

            m_listenSocket.Bind(m_listenEP);
            m_listenSocket.Listen(10);

            m_clientSocket = m_listenSocket.Accept();

           
            // 클라이언트로부터 객체 수신
            int LENGTH = 1024;
            int OFFSET = 0;
            byte[] socket_buffer = new byte[LENGTH];

            while (true)
            {
                int rcvd = m_clientSocket.Receive(socket_buffer, OFFSET, LENGTH, 0);
                Console.WriteLine("메세지 수신 : " + rcvd + "byte");
                if (rcvd > 0)
                {
                    ms.Write(socket_buffer, 0, rcvd);
                }
                if (rcvd < LENGTH)
                {
                    break;
                }
            }

            // 수신된 MemoryStream을 Base64String으로 변환
            string base64EncodedString = mc.MemoryStreamToBase64String(ms);

            // 객체 string을 DB에 삽입
            MySqlConnection con = new MySqlConnection("server = localhost; uid=root; password=1234; database=memojang; pooling=false;");
            MySqlCommand cmd = new MySqlCommand("UPDATE memo SET memo=?paramDATA WHERE id='dalili'", con);
            cmd.Parameters.Add(new MySqlParameter("?paramDATA", MySql.Data.MySqlClient.MySqlDbType.LongText)).Value = base64EncodedString;
           
            cmd.Connection.Open();
            int result = cmd.ExecuteNonQuery();
            Console.WriteLine("result = " + result);

            cmd.Dispose();
            con.Close();

            // DB에서 불러오기
            con = new MySqlConnection("server = localhost; uid=root; password=1234; database=memojang; pooling=false;");
            cmd = new MySqlCommand("SELECT * FROM memo WHERE id='dalili'", con);
            cmd.Connection.Open();
            MySqlDataReader mdr = cmd.ExecuteReader();
            mdr.Read();

            // 객체 string을 byte로 변환
            byte[] buffer2 = mc.Base64StringToByte(mdr.GetString(1));

            // 객체 byte를 클라이언트로 send
            m_clientSocket.Send(buffer2);

            cmd.Dispose();
            con.Close();

            m_listenSocket.Close();
            m_clientSocket.Close();

        }
    }





------------- 클라이언트 -------------


class Program
    {
        static void Main(string[] args)
        {
            MyConvert mc = new MyConvert();

            Console.WriteLine("클라이언트 시작");
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect("localhost", 555);

            MemoryStream ms = new MemoryStream();
            BinaryFormatter binFmtr = new BinaryFormatter();

            MyClass myClass = new MyClass();
            myClass.ID = "test";
            myClass.DATA = "hello world";

            // 객체를 ms로 변환
            ms = mc.ObjectToMemoryStream(myClass);

            int LENGTH = (int)ms.Length;
            int OFFSET = 0;
            byte[] buffer = new byte[LENGTH];

            while (true)
            {
                int rcvd = ms.Read(buffer, OFFSET, LENGTH);
                Console.WriteLine("전송 : " + rcvd + "byte");
                if (rcvd > 0)
                {
                    socket.Send(buffer, 0, rcvd, 0);
                }
                else
                {
                    break;
                }
            }
            Console.WriteLine("전송완료");

            // 서버 로부터 객체 수신
            int LENGTH2 = 1024;
            int OFFSET2 = 0;
            byte[] socket_buffer = new byte[LENGTH2];
           
            ms.Position = 0;  //처음부터 다시 읽기 위해서

            while (true)
            {
                int rcvd = socket.Receive(socket_buffer, OFFSET2, LENGTH2, 0);
                Console.WriteLine("메세지 수신 : " + rcvd + "byte");
                if (rcvd > 0)
                {
                    ms.Write(socket_buffer, 0, rcvd);
                }
                if (rcvd < LENGTH2)
                {
                    break;
                }
            }
            Console.WriteLine("수신완료");

            // ms 스트림을 객체로 변환
            MyClass myClass2 = (MyClass) mc.MemoryStreamToObject(ms);

            Console.WriteLine(myClass2.ID);
            Console.WriteLine(myClass2.DATA);

            ms.Close(); //소멸.
            socket.Close();
        }

Posted by penguindori