Connection 객체


    - 데이터 소스와의 물리적인 연결
    - 객체 속성은 데이터 공급자, 연결하고자 하는 데이터 소스, 연결 문자열로 구성
    - 객체 메소드는 연결의 Open 및 Close, 데이터베이스 변경 및 트랜잭션을 관리
    - 데이터공급자만 다를뿐 객체생성방법이나 속성, 메소드 이용방법은 같다.


    - 주요속성


        * ConnectionString  : 연결 문자열을 지정하거나 받음
        * ConnectionTimeout : Timeout을 받음(기본 15초)
        * DataBase  : 연결한 데이터베이스 이름
        * DataSource
                 OleDb : 데이타베이스 소스의 위치와 파일명
                 SQL : 연결할 SQL 서버 인스턴스 이름
        * Provider : OLE DB 공급자 이름
        * ServerVersion : 데이터베이스의 서버 버전
        * State  : 연결상태 (Broken/Closed/Connecting/Executing /Fetching/Open)
        * WorkstationId : 연결한 클라이언트 ID


    - 주요메서드


        * Open : 데이터베이스에 연결
        * ChangeDatabase : 연결된 데이터베이스 변경
        * BeginTransaction : DB의 트랜잭션 시작을 지정
        * CreateCommand : Command 객체 생성
        * Close : 데이터베이스 연결 해제


    - 이벤트


        * StateChange : .NET data provider에 의한 연결 상태 변경 시 발생
        * InfoMessage  : SQL Server가 경고나 정보 제공 메시지를 반환할 때 발생


    -  Connection string parameters


        * Provider (OLE DB only)
                Access : Microsoft.JET.OLEDB.4.0
                Oracle - MSDAORA
                MS SQL - SQLOLEDB
        * Data Source(server) :  데이터베이스 위치 (Domain or IPAddress)
        * Initial Catalog (database): 데이터 베이스 이름
        * User ID/Password : 인증 정보


    - OleDbConnection예제


        using System;
        using System.Data;
        using System.Data.OleDb;

        namespace Connection
        {
            class Class1
            {
                static void Main(string[] args)
                {
                    string strConn ="Provider=Microsoft.Jet.OLEDB.4.0;"

                   + @"Data Source=C:\Inetpub\iissamples\sdk\asp\database\authors.mdb;";


                    OleDbConnection conn = new OleDbConnection(strConn);


                    // or
                    // OleDbConnection conn = new OleDbConnection();
                    // conn.ConnectionString =  strConn;


                    conn.Open();

                    Console.WriteLine("Database = \t\t" + conn.Database);
                    Console.WriteLine("DataSource = \t\t" + conn.DataSource);
                    Console.WriteLine("DataServerVersion = \t" + conn.ServerVersion);
                    Console.WriteLine("State = \t\t" + conn.State);


                    conn.Close();
                    Console.WriteLine("State = \t\t" + conn.State);


                    Console.ReadLine();
                }
            }
        }


    - SqlConnection예제


        using System;
        using System.Data;
        using System.Data.SqlClient;

        namespace Connection
        {
            class Class1
            {
                static void Main(string[] args)
                {
                    string strConn;
                    strConn = "Data Source=localhost;Initial Catalog=northwind;"

                                  + "User ID=sa;Password=sy230;Integrated Security=SSPI;";
                    //strConn = "Server=localhost;Database=NorthWind;Uid=sa;Pwd=1111";


                    SqlConnection conn = new SqlConnection(strConn);


                    // or
                    // SqlConnection conn = new SqlConnection();
                    // conn.ConnectionString =  strConn;


                    conn.Open();

                    Console.WriteLine("Database = \t\t" + conn.Database);
                    Console.WriteLine("DataSource = \t\t" + conn.DataSource);
                    Console.WriteLine("DataServerVersion = \t" + conn.ServerVersion);
                    Console.WriteLine("State = \t\t" + conn.State);
                    Console.WriteLine("WorkstationID = \t" + conn.WorkstationId +"\n");


                    conn.ChangeDatabase("pubs");
                    Console.WriteLine("Database = \t\t" + conn.Database);   

       

                    conn.Close();
                    Console.WriteLine("State = \t\t" + conn.State);


                    Console.ReadLine();
                }
            }
        }


Command 객체


    - 데이터 입력, 수정, 삭제 등의 모든 명령을 처리
    - 데이터 소스에 대한 SQL문, Stored procedure를 표시
    - Connection 객체와 상관없이 생성하고 실행 가능
    - 데이터 소스와 DataSet과의 통신을 위해 DataAdapter 객체가 Command 객체 사용
    - 데이터공급자만 다를뿐 객체생성방법이나 속성, 메소드 이용방법은 같다.


    - 생성자


        * SQL Server 7.0  :
                SqlCommand comm = SqlCommand(cmdTxt, sqlCon);
                //or
                SqlCommand comm = new SqlCommand(cmdTxt);
                comm.Connection = sqlCon;
                //or
                SqlCommand comm = new SqlCommand();
                comm.Connection = sqlCon;
                comm.CommandText = cmdTxt;
                //or
                SqlCommand  comm = sqlCon.CreateComand();  
                comm.CommandText = cmdTxt; 


        * OLEDB
                OleDbCommand comm = OleDbCommand(cmdTxt, oleDbCon);
                //or

                OleDbCommand comm = new OleDbCommand(cmdTxt);
                comm.Connection = oleDbCon;
                //or

                OleDbCommand comm = new OleDbCommand();
                comm.Connection = oleDbCon;
                comm.CommandText = cmdTxt;
                //or
                OleDbCommand  comm = sqlCon.CreateComand();
                comm.CommandText = cmdTxt; 


    - 주요속성


        * Connection : Command 객체가 사용한 Connection 개체 반환 및 설정.
        * CommandTimeOut : 명령어가 걸리는 시간 지정 또는 받음(Default : 30초)
        * Transaction : 명령어 실행에 트랜잭션을 지정하거나 받음
        * UpdateRowSource :   업데이트 중인 행에 쿼리 명령 결과를 적용하는 방법을 지정.


                - Both : 출력 매개 변수와 처음 반환된 행은 모두 DataSet의 변경된 행에 매핑.
                - FirstReturnedRecord  : 처음 반환된 행의 데이터는 DataSet의 변경된 행에 매핑.
                - None : 반환된 매개 변수와 행이 무시.
                - OutputParameters  : 출력 매개 변수는 DataSet의 변경된 행에 매핑.

                //명령이 자동으로 생성되지 않는 한 기본 UpdateRowSource 값은 Both.
                // 명령이 자동으로 생성되면 기본값은 None


        * CommandText  : SQL 문이나  저장 프로시저 반환 및  설정.


           SQL : myCom.CommandText = "SELECT * FROM authors WHERE au_id = @id";
           OLEDB, ODBC :
                myCom.CommandText = "SELECT * FROM authors WHERE au_id = ?";


              -  OLEDB, ODBC를 사용할 경우 실행할 명령문의 ?순서와

                 파라미터 추가하는 순서는 같아야 한다.


        * Parameters : Transact-SQL 문이나 지정 프로시저의 매개 변수
               - ParameterName : 매개변수 이름
               - SqlDBType: 매개변수의 데이타베이스의 필드타입
               - Value : 매개변수의 값 설정 및 확인
               - Direction :  Input/Output/InputOutput. Default는 Input.


        * CommandType
                - CommandText 속성을 해석하는 방법을 나타내는 값을 가져오거나 설정.
                - Text : SQL 텍스트 명령, Default
                - StoredProcedure : 저장 프로시저의 이름
                - TableDirect : 테이블명


            [Text 사용예제]
             SqlCommand cmd=new SqlCommand("delete emp where id=@id", conn);
             cmd.Parameters.Add("@id", SqlDbType.NVarChar, 15).Value = "London";


             [StoredProcedure 사용예제]
             sqlCom.CommandText = "sp_ReturnName";
             sqlCom.CommandType = CommandType.StoredProcedure;
             sqlCom.Parameters.Add("@au_id",SqlDbType.VarChar,20).Value="527-72-3246";


             [TableDirect 사용예제]
             sqlCom.CommandText = "Users";
             sqlCom.CommandType = CommandType.TableDirect;
               - TableDirect는 .NET Framework Data Provider for OLE DB에서만 지원.
               - Execute 메서드 중 하나를 호출하면 명명된 테이블의 행과 열이 모두 반환.
               - 여러 테이블에 액세스하려면 공백없이 테이블의 이름이 쉼표로 연결된 목록 사용.
               - 여러테이블의 이름을 지정하면 지정된 테이블이 조인이 반환.


    - 메소드


       * Cancel : 명령어 취소
       * Prepare : 데이터 소스에 명령의 준비 버전이나 컴파일 버전을 만듭니다
       * ExecuteNonQuery : 명령어 실행
       * ExecuteReader : 명령어를 Connection 통해 보내고 SqlDataReader 바인딩
       * ExecuteScalar : 명령어 실행, Sum(), Avg()와 같이 값이 하나만 반환될 경우 사용
       * ExecuteXmlReader : 명령어를 Connection을 통해 보내고 XmlReader 바인딩
        // 더 자세한 내용은 DataReader 참고


       - ExecuteNonQuery를 사용한 예제


        private void CommandEx()
        {
            string sqlStr = "Data Source=localhost;Initial Catalog=northwind;"

                              + "User ID=sa;Password=1111;";


            SqlConnection sqlCon = new SqlConnection(sqlStr);
            try
            {
                sqlCon.Open();
                MessageBox.Show("현재 상태 :" + sqlCon.State);
            }
            catch(SqlException ex)
            {
                MessageBox.Show(ex.Message);
            }


            try
            {
                SqlCommand sqlCom = sqlCon.CreateCommand();


                sqlCom.CommandText="update region set regiondescription = @name1 "

                                             + " where regiondescription = @name2";

                sqlCom.Parameters.Add("@name1", SqlDbType.VarChar, 50).Value =  "name1Value";
                sqlCom.Parameters.Add("@name2", SqlDbType.VarChar, 20).Value =  "name2Value";


                sqlCom.Prepare();

                int result = sqlCom.ExecuteNonQuery();


                Console.WriteLine("영향받는 행의 개수는 : {0}", result);
            }
            catch(SqlException ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                sqlCon.Close();
            }
        }

DataReader 객체


 - 데이터 소스의 데이터를 읽기 전용으로 포함하기 위한 빠르고 오버헤드가 적은 객체
 - WinForm or WebForm에 데이타베이스 쿼리를 통해 얻은 결과를  출력
 - Command 객체의 ExcuteReader 메소드로만 객체 생성
 - 연결지향, 빠른 속도, 읽기전용, 한 테이블만 이용
 - 쿼리에서 리턴된 구조 데이터 레코드 제공


[참고]
Read 메서드를 호출하면 요청할 때까지 클라이언트의 네트워크 버퍼에 저장, 쿼리의 전체 결과가 반환될 때까지 기다리는 것이 아니라 사용이 가능해 지면 곧바로 데이터를 검색하는 동시에 기본적으로 메모리에 한 번에 행 하나씩만 저장하여 시스템 오버헤드를 줄임으로써 응용 프로그램의 성능을 향상시킬 수 있습니다.


DataReader는 프로시저 논리가 데이터 소스에서 순차적으로 가져오는 결과를 효율적으로 처리할 수 있도록 버퍼링되지 않은 데이터 스트림을 제공합니다. 대량의 데이터를 검색할 때에는 데이터가 메모리에 캐싱되지 않으므로 DataReader를 선택하는 것이 좋습니다.


 ● 주요속성


        * HasRows : DataReader에서 읽기 전에 결과가 반환되는지 여부를 판별

        * FieldCount  : 현재 행의 열개수
        * IsClosed :  데이터 판독기가 닫혔는지 여부
        * RecordsAffected : Transact-SQL 문의 실행에 의해 변경, 삽입 및 삭제된 행의 개수
        * Depth : 현재 행의 중첩 수준을 나타내는 값


 ● 주요메소드


        * Close : DataReader 개체를 닫습니다.
        * GetDataTypeName : 소스 데이터 형식의 이름을 가져온다.
        * GetFieldType : 개체의 데이터 형식인 Type을 가져온다.
        * GetName : 지정된 열의 이름을 가져온다.
        * GetOrdinal : 열 이름이 지정된 경우 열 서수를 가져온다.
        * GetSchemaTable : DataReader의 열 메타데이터를 설명하는 DataTable을 반환.
        * IsDBNull  : 열이 존재하지 않거나 없는 값을 포함하는지 여부를 나타내는 값을 가져온다.
        * NextResult : 여러개의 테이블을 Transact-SQL 문의 결과를 읽을 때,

                            데이터 판독기를 다음 결과 테이블로 이동.
        * Read : 각 Record를 읽기 위한 메소드, record가 없을 때는 false 리턴,

                    있을때는 true 이면서 레코드값을 가짐.


            [필드 접근방법]
              * 객체명["필드명"]
              * 객체명[필드순서]
              * Get 메소드를 이용하여  네이티브 데이터 형식의 열 값에 액세스
                   : (GetChar, GetDateTime, GetInt32, GetFloat, GetString, GetGuid, …)
                   : 객체명.GetString(필드순서)

                      -> 필드순서에 해당하는 열값의 자료형을 String형식으로 가져온다.
                   : 열값을 검색할 때 필요한 형식 변환의 양이 줄어듬으로 성능향상에 도움이 된다.


 ● DataReader 객체를 이용한 데이터 접근 예


        string strConn = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\data\myDB.mdb";
        string strSQL = "SELECT LastName, Age From User";
        OleDbConnection conn = New OleDbConnection(strConn);
        OleDbCommand comm = new OleDbComand(strSQL, conn);
        try{
              conn.Open(); 
              Console.WriteLine("DB연결상태 : "+conn.State);

              OleDbDataReader reader = comm.ExcuteReader();
              while(reader.Read() == true)
              {
                   Console.WriteLine(reader["LastName"] + " " + reader["Age"]);

                   //Console.WriteLine(reader.GetString(0) +" " + reader.GetInt32(1));
                   //Console.WriteLine(reader[0] + " " + reader[0]);

               }
              reader.Close();
              conn.Close();
        }
        catch (Exception e)
        {
            Console.WriteLine("데이터베이스 열기 실패")l
        }


 ● DataReader 객체를 이용하여 여러 결과 집합가져오기


        string cmdTxt = "SELECT CategoryID, CategoryName FROM Categories;"
                              + "SELECT EmployeeID, LastName FROM Employees";
        SqlCommand comm  = new SqlCommand(cmdTxt, conn);
        conn.Open();
        SqlDataReader myReader = comm.ExecuteReader();

        do{
            Console.WriteLine("\t{0}\t{1}", myReader.GetName(0), myReader.GetName(1));

            while (myReader.Read())
                Console.WriteLine("\t{0}\t{1}", myReader.GetInt32(0), myReader.GetString(1));

        }while (myReader.NextResult());

        myReader.Close();
        conn.Close();


 ● DataReader에서 스키마 정보 가져오기


        /*

        DataReader가 열려 있는 동안에는 GetSchemaTable 메서드를 사용하여

        현재 결과 집합에 대한 스키마 정보를 검색할 수 있습니다.
        GetSchemaTable은 현재 결과 집합에 대한 스키마 정보를 포함하는 행과 열로 채워진

        DataTable 개체를 반환합니다.
        DataTable은 결과 집합의 각 열마다 한 행씩 포함하게 됩니다.
        스키마 테이블 행의 각 열은 결과 집합에 반환된 열의 속성에 매핑됩니다.
        여기서 ColumnName은 속성 이름이고 열 값은 속성 값입니다.

        */


        DataTable schemaTable = myReader.GetSchemaTable();

        foreach (DataRow myRow in schemaTable.Rows)
        {
            foreach (DataColumn myCol in schemaTable.Columns)
                Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);

            Console.WriteLine();
        }
       
        using System;
using System.Data;
using System.Data.SqlClient;


namespace ADO.NET.STUDY
{
    class MainClass
    {
        private string ConString = null;
        private SqlConnection SqlCon = null;
        private SqlCommand SqlCom = null;
        private SqlDataReader sReader = null;
       
        public MainClass()
        {
            this.ConString = "Server=Localhost;database=Pubs;UID=sa;PWD=1111";
        }


        [STAThread]
        static void Main(string[] args)
        {
            MainClass main = new MainClass();

            main.use_ExcuteReader();
            main.use_ExecuteScalar();
            main.use_getSqlReturnValue();
            main.use_Transaction();

            Console.Read();     //실행화면 확인을 위한 일시중지
        }


        private void DBConnect()
        {
            if(SqlCon == null)
                SqlCon = new SqlConnection(ConString);

            if(SqlCon.State == ConnectionState.Closed)
                try
                {
                    SqlCon.Open();
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.Message);
                }
        }


        private void DBClose()
        {
            if(SqlCon.State != ConnectionState.Closed)
                SqlCon.Close();
        }


        // 저장프로시저를 이용한 ExcuteReader, ExecuteNonQuery사용예제
        // 저장프로시저 매개변수 사용법 및 OUTPUT, RETRUN값 받는 예제 포함.
        private void use_ExcuteReader()
        {
            this.DBConnect();


            string strSQLSel = "sp_getUser";
            //string strSQLUpdate = "UPDATE authors SET au_fname = @fname TEL = @tel WHERE au_lname = @lname";


            /*
             *  프로시저 sp_getUesr의 내용
             *  create proc sp_getUser(@state varchar(20), @Cnt int output) as
             *      SELECT au_id, phone  FROM authors WHERE state = @state
             *      SET @Cnt = @@rowcount
             */
               
            try
            {
                this.DBConnect(); // DB연결하는 메소드
                   
                SqlCom = new SqlCommand();
                SqlCom.Connection = SqlCon;
                SqlCom.CommandTimeout = 15;
                // 명령 실행을 종료하고 오류를 생성하기 전 대기 시간, 기본값은 30초

                //SqlCom.CommandText = "SELECT au_id, phone  FROM authors WHERE state = @state";
                SqlCom.CommandText = strSQLSel;
                SqlCom.CommandType = CommandType.StoredProcedure;
                //명령줄의 형식은 프로시저임,

                //일반쿼리문임을 나타내는 Text타입를 제외한 나머지 타입인 경우는 꼭 기재해야함.


                SqlCom.Parameters.Add("@state", SqlDbType.VarChar, 20).Value = "CA";
                //프로시저의  @state매개변수의 값에 "CA"라는 이름을 넣는다


                SqlCom.Parameters.Add("@cnt", SqlDbType.Int, 4);
                SqlCom.Parameters["@cnt"].Direction = ParameterDirection.Output;
                /*  또는
                 *  SqlParameter spName = new SqlParameter ();
                 *  spName.ParameterName = "@state";
                 *  spName.SqlDbType =  SqlDbType.VarChar;
                 *  spName.Size = 20;
                 *  spName.Value = "CA";
                 *  spName.SourceVersion = DataRowVersion.Current;
                 *  SqlCom.Parameters.Add(spName);
                 */


                int cnt = SqlCom.ExecuteNonQuery();
                /* ExecuteNonQuery : 해당쿼리문에 적용된 레코드의 개수 반환
                 *
                 * SQL 문에서는 기본적으로 해당 명령(INSERT, DELETE, UPDATE)문의
                 * 영향을 받은 레코드 수를 나타내는 정수 값이 반환이 되는데 ExecuteNonQuery에서
                 * 바로 그 값을 받는것이다.
                 *
                 * 참고 : INSERT, DELETE, UPDATE문이 아닌 다른 명령문(SELECT), 또는 롤백이 일어났을 경우 -1이 리턴.
                 */


                Console.WriteLine("ExecuteNonQuery에서 적용받은 레코드의 수 : {0}", cnt);
                // SELECT문을 이용했기때문에 기본적으로 return되는 값은 -1

                Console.WriteLine("Output으로 받은 레크드의 개수 :" + SqlCom.Parameters["@cnt"].Value);
                // SELECT한 레코드의 수를 가져오기위해서는 위처럼 OUTPUT파라미터 이용


                /*
                 * System.SqlClient.SqlCommand.ExecuteReader를 호출하여 반환되는
                 * Output, InputOut 및 ReturnValue 매개 변수에 액세스하려면 SqlDataReader에서
                 * System.SqlClient.SqlDataReader.Close 또는 Dispose를 호출해야 합니다.(도움말)
                 *
                 * 위 명령의  경우 ExecuteNonQuery는 Close를 할 필요가 없기때문에 매개변수값이 출력됩니다.
                 * 이 소스의 아래부분에 있는 출력매개변수 출력부분을 sReader.Close() 명령이 일어나기 전에 넣어서 테스트해보세요.
                 *
                 *  참고 : 반환되는 값이 출력매개변수 값 하나이면 Close를 먼저 하지 않아도 결과값을 받을수 있습니다.
                 */
              
                sReader = SqlCom.ExecuteReader();
                //authors테이블에서 state열 값이 "CA"인 레코드 반환


                while (sReader.Read())
                {
                    Console.WriteLine(sReader.GetString(0) + ", " + sReader.GetString(1));
                    // 반환된 레코드중 첫번째필드(au_id)와 두번째필드(phone)값 콘솔에 출력
                }

                sReader.Close();    // DataReader사용후 꼭 닫아줘어야 함.

                Console.WriteLine("Output으로 받은 레크드의 개수 : " + SqlCom.Parameters["@cnt"].Value);
                // SELECT한 레코드의 수를 가져오기위해서는 위처럼 OUTPUT파라미터 이용

                this.DBClose();     // DB연결을 닫는 메소드
            }
            catch (Exception ec)
            {
                Console.WriteLine(ec.Message);
            }
        }


        /* ExecuteScalar 이용한 간단한 예제 */
        private void use_ExecuteScalar()
        {
            SqlCon = new SqlConnection(ConString);
            try
            {
                SqlCon.Open();
            }
            catch(Exception ec)
            {
                Console.WriteLine(ec.Message);
            }

            SqlCom = SqlCon.CreateCommand();
            SqlCom.CommandText = "select count(au_id) from authors";
            SqlCom.CommandType = CommandType.Text;  // Default, 생략가능


            int result = (int) SqlCom.ExecuteScalar();
            Console.WriteLine("authors테이블의 레크드 개수 : "+ result.ToString());

            this.DBClose();
        }

       
        /* SQL의 사용자정의 함수의 Return값 받는 예제 */
        private void use_getSqlReturnValue()
        {
            this.DBConnect();

            SqlCom = SqlCon.CreateCommand();
            SqlCom.CommandText = "sp_ReturnName";
            SqlCom.CommandType = CommandType.StoredProcedure;


           /*
            * create function sp_ReturnName(@au_id varchar(20))
            *   returns varchar(30)
            *       as
            *       begin
            *           declare @name varchar(30)
            *           select @name = au_fname + ' ' + au_lname  from authors where au_id=@au_id
            *           return(@name)
            *       end
            *       go
            */


            SqlCom.Parameters.Add("@au_id", SqlDbType.VarChar, 20).Value = "527-72-3246";

            SqlParameter sampParm = SqlCom.Parameters.Add("ReturnValue", SqlDbType.VarChar, 30);
            sampParm.Direction = ParameterDirection.ReturnValue;


            // ExecuteReader를 호출하여 매개변수를 반환받는다.
            // ParameterDirection 이 출력이면, 관련 SqlCommand의 실행은 값을 반환하지 않으며,
            // SqlParameter는 null 값을 포함하지 않습니다.


            sReader = SqlCom.ExecuteReader();
            string result = (string) SqlCom.Parameters["ReturnValue"].Value; 
            Console.WriteLine("프로시저의 리턴값: {0}", result);
            result = "이세영";

            sReader.Close();        
           
            this.DBClose();
           
        }


        /* 트랜잭션사용예제 */
        private void use_Transaction()
        {
            this.DBConnect();              
            SqlTransaction myTrans = SqlCon.BeginTransaction();   // 트랜잭션 처리 시작
            SqlCom = SqlCon.CreateCommand();
            SqlCom.Transaction = myTrans;
            try
            {
                SqlCom.CommandText = "update titleauthor set  au_ord = 3 where au_id = '172-32-1176'";
                int updateCnt = (int) SqlCom.ExecuteNonQuery();
                Console.WriteLine("업데이트수행 결과 : " + updateCnt);

                // myTrans.Save("SavePointPosition");
                // 트랜잭션의 흐름 중에서 전체를 롤백하고 싶지 않을 경우 표시한 부분까지만 롤백하고자 할 때

                SqlCom.CommandText = "insert into titleauthor (au_id, title_id, au_ord, royaltyper) values('111-11-1111', 'AB0001', 3, 50)";
                int insertCnt = (int) SqlCom.ExecuteNonQuery();
                Console.WriteLine("업데이트수행 결과 : " + insertCnt);

                myTrans.Commit();
                // 현재 위 쿼리가 모두 수행되었을때만 트랜젝션 완료.
                // 하나라도 오류발생하면 수행했던 모든 실행 취소
                Console.WriteLine("update와 insert가 정상적으로 완료되었습니다.");
            }
            catch(Exception e)
            {
                try
                {
                    myTrans.Rollback();
                    // myTrans.Rollback("SavePointPosition");
                    // SavePointPosition의 위치까지만 Rollback한다고 선언.
                    // Savepoint 이전의 명령들은 처리가 된다

                }
                catch (SqlException ex)
                {
                    if (myTrans.Connection != null)
                    {
                        Console.WriteLine(ex.GetType());
                    }
                }
                Console.WriteLine(e.GetType());
                Console.WriteLine("update와 insert명령이 둘다 완료되지 못했습니다.");
            }
            finally
            {
                this.DBClose();
            }
        }
    }
}
 

DataTable.Select Method


필터 조건 및 지정된 상태와 일치하는 모든 DataRow 개체의 배열을 정렬 순서대로 가져옵니다.


[C#]
public DataRow[] Select(
   string filterExpression,                 // 행을 필터링하기 위해 사용하는 조건입니다.
   string sort,                                   // 열과 정렬 방향을 지정하는 문자열입니다.
   DataViewRowState recordStates //DataViewRowState 값 중 하나로 DataRow의 버전.
);


* 반환값 : DataRow 개체로 이루어진 배열입니다.

* 행을 필터링하기 위한 조건식은 DataColumn 클래스의 Expression 속성을 참조하십시오.


private static void GetRowsByFilter()

    DataTable customerTable = new DataTable( "Customers" );
    customerTable.Columns.Add( "id", typeof(int) );
    customerTable.Columns.Add( "name", typeof(string) );

    customerTable.Columns[ "id" ].Unique = true;
    customerTable.PrimaryKey = new DataColumn[] { customerTable.Columns["id"] };

    for( int id=1; id<=10; id++ )
    {
        customerTable.Rows.Add(
            new object[] { id, string.Format("customer{0}", id) } );
    }

    customerTable.AcceptChanges();

    for( int id=11; id<=20; id++ )
    {
        customerTable.Rows.Add(
            new object[] { id, string.Format("customer{0}", id) } );
    }

    string strExpr;
    string strSort;
   
    strExpr = "id > 5";
    // Sort descending by column named CompanyName.
    strSort = "name DESC";
    // Use the Select method to find all rows matching the filter.
    DataRow[] foundRows =
        customerTable.Select( strExpr, strSort, DataViewRowState.Added );

    // id>5 조건에 맞는 행만 찾아서 name컬럼을 내림차순으로 정렬하여 가져온다.
   
    PrintRows( foundRows, "filtered rows" );

    foundRows = customerTable.Select();  //DataTable의 모든 행을 가져온다.
    PrintRows( foundRows, "all rows" );
}

private static void PrintRows( DataRow[] rows, string label )
{
    Console.WriteLine( "\n{0}", label );
    if( rows.Length <= 0 )
    {
        Console.WriteLine( "no rows found" );
        return;
    }
    foreach( DataRow r in rows )
    {
        foreach( DataColumn c in r.Table.Columns )
        {
            Console.Write( "\t {0}", r[c] );
        }
        Console.WriteLine();
    }
}

DataColumn.Expression 속성


행을 필터링하거나 열의 값을 계산하거나 집계 열을 만드는 데 사용되는 식을 가져오거나 설정합니다.


Expression 속성을 사용하는 경우 중 하나는 계산 열을 만들 때입니다.
예를 들어, 과세 가격을 계산하려면 단가에 해당 지역의 세율을 곱합니다.
세율은 지역에 따라 다르기 때문에 열에 단일 세율을 넣기가 불가능하므로
아래 Visual Basic 코드에 표시된 것처럼 Expression 속성을 사용하여 값을 계산합니다.


DataSet1.Tables("Products").Columns("tax").Expression = "UnitPrice * 0.086"


또 다른 경우는 집계 열을 만들 때입니다.


계산 값과 마찬가지로 집계도 DataTable의 전체 행 집합을 기반으로 작업을 수행합니다.
간단한 예로는 집합에 반환되는 행의 수 계산이 있으며, 다음 Visual Basic 코드에 표시된 것처럼

특정 판매원이 완료한 거래의 수를 계산할 때 사용하는 메서드입니다.


DataSet1.Tables("Orders").Columns("OrderCount").Expression = "Count(OrderID)"


식 구문


식을 만드는 경우 ColumnName 속성을 사용하여 열을 참조합니다.
예를 들어, 두 열의 ColumnName 이 각각 "UnitPrice"와 "Quantity"인 경우

식은 다음과 같습니다.


"UnitPrice * Quantity"


필터에 대한 식을 만드는 경우 문자열을 작은따옴표로 묶습니다.


"LastName = 'Jones'"


다음 문자들은 특수 문자이며 아래에 설명된 것처럼 열 이름에 사용할 경우

이스케이프되어야 합니다.


\n(줄 바꿈), \t(탭), \r(캐리지 리턴), ~, (, ), #, \, /, =, >, <, +, -, *, %, &, |, ^, ', ", [, ]


열 이름에 위 문자 중 하나가 있는 경우 해당 이름은 대괄호로 묶어야 합니다.
예를 들어, 식에 열 이름 "Column#"을 사용하려면 "[Column#]"이라고 쓰면 됩니다.


Total * [Column#]


대괄호는 특수 문자이기 때문에 열 이름에 포함되는 경우 슬래시("\")를 사용하여 이스케이프해야 합니다. 예를 들어, 열 이름이 "Column[]"이면 다음과 같이 씁니다.


Total * [Column[\]]


둘째 대괄호만 이스케이프해야 합니다.


사용자 정의 값


사용자 정의 값은 열 값을 비교하는 식에 사용될 수 있습니다. 문자열 값은 작은따옴표로 묶어야 합니다.  날짜 값은 파운드 기호(#)로 묶어야 합니다.


10진수 및 지수 표기법으로 숫자 값을 표시할 수 있습니다. 예를 들면 다음과 같습니다.


"FirstName = 'John'"

"Price <= 50.00"

"Birthdate < #1/31/82#"


열거형 값이 포함된 열에서는 값을 정수 데이터 형식으로 캐스팅합니다.

예를 들면 다음과 같습니다.


"EnumColumn = 5"


연산자


Boolean AND, OR 및 NOT 연산자를 사용하여 연결할 수 있습니다.
괄호를 사용하여 절을 묶어서 우선 순위를 적용할 수 있습니다.
AND 연산자는 다른 연산자에 우선합니다. 예를 들면 다음과 같습니다.


(LastName = 'Smith' OR LastName = 'Jones') AND FirstName = 'John'


비교식을 만들 때 허용되는 연산자는 다음과 같습니다.


< , > , <= , >= , <> , = , IN , LIKE


또한 다음과 같은 산술 연산자가 지원됩니다.


+(더하기) , -(빼기) , *(곱하기) , /(나누기) , %(나머지)


문자열 연산자


문자열을 연결하려면 + 문자를 사용합니다.
문자열 비교 시 대/소문자 구별 여부는 DataSet 클래스의 CaseSensitive 속성 값에 의해 결정되지만 DataTable 클래스의 CaseSensitive 속성이 이 값에 우선합니다.


와일드카드 문자


*와 %는 모두 LIKE 비교에서 와일드카드로 교대로 사용할 수 있습니다.
LIKE 절의 문자열에 * 또는 %가 있으면 이들 문자는 대괄호([])로 이스케이프해야 합니다.
대괄호가 절에 있으면 대괄호 문자를 대괄호로 이스케이프해야 합니다.
(예: [[] 또는 []]). 와일드카드는 패턴의 시작과 끝 부분, 패턴의 끝 부분 또는 패턴의 시작 부분에 사용할 수 있습니다. 예를 들면 다음과 같습니다.


"ItemName LIKE '*product*'"

"ItemName LIKE '*product'"

"ItemName LIKE 'product*'"


문자열의 중간에는 와일드카드를 사용할 수 없습니다.
예를 들어 'te*xt'는 사용할 수 없습니다.


부모/자식 관계 참조


열 이름 앞에 Parent를 붙여 식에서 부모 테이블을 참조할 수 있습니다. 예를 들어, Parent.Price 는 이름이 Price 인 부모 테이블의 열을 참조합니다.


열 이름 앞에 Child를 붙여 식에서 자식 테이블에 있는 열을 참조할 수 있습니다. 그러나 자식 관계는 여러 행을 반환할 수 있으므로 자식 열에 대한 참조를 집계 함수에 포함시켜야 합니다.  예를 들어, Sum(Child.Price) 은 자식 테이블에서 이름이 Price 인 열의 합을 반환합니다. 테이블에 여러 자식이 있는 경우 구문은 Child(RelationName)입니다.


예를 들어, 테이블에 이름이 Customers 및 Orders 인 자식 테이블과 Customers2Orders 라는 DataRelation 개체가 있으면 참조는 다음과 같습니다.


Avg(Child(Customers2Orders).Quantity)


집계


다음과 같은 집계 형식이 지원됩니다.


Sum(합계), Avg(평균), Min(최소값), Max(최대값), Count(개수),

StDev(통계적 표준 편차), Var(통계적 분산)


집계는 일반적으로 관계를 따라 수행됩니다. 위에 나열한 함수 중 하나와 위의 부모/자식 관계 참조에서 설명한 자식 테이블 열 하나를 사용하여 집계식을 만듭니다.
예를 들면 다음과 같습니다.


Avg(Child.Price)

Avg(Child(Orders2Details).Price)


단일 테이블에서 집계를 수행할 수도 있습니다.

예를 들어, 이름이 "Price"인 열의 수치 집계를 만드는 식은 다음과 같습니다.


Sum(Price)


단일 테이블을 사용하여 집계를 만드는 경우 그룹화 기능이 없는 대신 열의 모든 행에 같은 값이 표시됩니다. 테이블에 행이 없으면 집계 함수는 null 참조(Visual Basic의 Nothing)을 반환합니다.

DataTable.Compute 메서드

필터 조건을 전달하는 현재 행에서 지정된 식을 계산합니다.


public object Compute
(
   string expression,  // 계산할 식
   string filter   // 식에서 계산하는 행을 제한할 필터
);


*반환 값 : 계산 결과로 설정된 Object를 반환


expression 매개 변수에는 집계 함수가 필요합니다. 예를 들어, 다음은 올바른 식입니다.


Count(Quantity)


그러나 다음은 잘못된 식입니다.


Sum (Quantity * UnitPrice)


두 개 이상의 열에서 작업을 수행해야 하는 경우, DataColumn을 만들고 해당 Expression 속성을 적절한 식으로 설정한 다음 결과 열에 집계식을 사용해야 합니다.


예를 들어, DataColumn의 이름이 "total"이고 Expression 속성이 다음과 같이 설정된 것으로 가정합니다.


"Quantity * UnitPrice"


이러한 경우 Compute 메서드에 대한 식 인수는 다음과 같습니다.


Sum(total)


두 번째 매개 변수 filter 는 식에서 사용될 행을 결정합니다.
예를 들어, 테이블에 "colDate"라는 이름의 날짜 열이 있으면 다음 식을 사용하여 행을 제한할 수 있습니다.


colDate > 1/1/99 AND colDate < 17/1/99


두 매개 변수에 대한 식을 만드는 규칙에 대한 자세한 내용은 DataColumn 클래스의 Expression 속성을 참조하십시오.


다음 예제에서는 ID 번호가 5인 판매 사원에 대해 "Total" 열 값의 합계를 구합니다.


private void ComputeBySalesSalesID(DataSet myDataSet)

{
    // Presumes a DataTable named "Orders" that has a column named "Total."
    DataTable myTable;
    myTable = myDataSet.Tables["Orders"];
    // Declare an object variable.
    object objSum;
    objSum = myTable.Compute("Sum(Total)", "EmpID = 5");
 }
DataRelation 은 DataColumn 개체를 통해 두 개의 DataTable 개체를 서로 연결하는 데 사용됩니다. 예를 들어 Customer/Orders 관계에서 Customers 테이블은 부모이고 Orders 테이블은 관계의 자식입니다. 기본 키/외래 키 관계와 비슷합니다.


부모 및 자식 테이블의 일치하는 열 간에 관계가 만들어집니다. 즉, 두 개의 열에 대한 DataType 값이 동일해야 합니다.


DataRelation의 주요한 기능 중 하나는 DataSet 내의 한 DataTable에서 다른 DataTable로 이동을 허용한다는 것입니다. 따라서, 연관된 DataTable의 DataRow가 하나 주어지면 연관된 다른 DataTable에 있는 모든 연관된 DataRow 개체를 검색할 수 있습니다. 예를 들어, 고객 테이블과 주문 테이블 사이에 DataRelation을 만든 다음 DataRow.GetChildRows를 사용하여 특정 고객 행에 대한 모든 주문 행을 검색할 수 있습니다.


DataRelation custOrderRel = custDS.Relations.Add("CustOrders",
                     custDS.Tables["Customers"].Columns["CustomerID"],
                     custDS.Tables["Orders"].Columns["CustomerID"]);


foreach (DataRow custRow in custDS.Tables["Customers"].Rows)
{
  Console.WriteLine(custRow["CustomerID"]);
  foreach (DataRow orderRow in custRow.GetChildRows(custOrderRel))
    Console.WriteLine(orderRow["OrderID"]);
}


Customers 및 Orders 테이블에 대해 DataRelation을 만들면 createConstraints 플래그(기본값은 true)의 값이 지정되지 않습니다. 이런 경우에는 Orders 테이블의 모든 행이 부모 테이블인 Customers에 존재하는 CustomerID 값을 가지고 있다고 가정합니다. Customers 테이블에 없는 CustomerID가 Orders 테이블에 있으면 ForeignKeyConstraint에 의해 예외가 throw됩니다.


부모 열에 포함되어 있지 않은 값이 자식 열에 있는 경우 DataRelation을 추가할 때 createConstraints 플래그를 false로 설정합니다. 아래 예제에서는 Orders 테이블과 OrderDetails 테이블 사이의 DataRelation에 대해 createConstraints 플래그가 false로 설정됩니다. 이렇게 되면 응용 프로그램에서는 런타임에 예외를 발생시키지 않고 OrderDetails 테이블의 모든 레코드와 Orders 테이블 레코드의 일부분만 반환할 수 있습니다.


DataRelation orderDetailRel = custDS.Relations.Add("OrderDetail",
                     custDS.Tables["Orders"].Columns["OrderID"],
                     custDS.Tables["OrderDetails"].Columns["OrderID"], false);



[에러내용] *********************************************************

이 열에는 현재 고유 값이 없습니다.
설명: 현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 발생했습니다. 스택 추적을 검토하여 발생한 오류 및 코드에서 오류가 발생한 위치에 대한 자세한 정보를 확인하십시오.

예외 정보: System.ArgumentException: 이 열에는 현재 고유 값이 없습니다.

인자를 가지는 메소드
- 입력인자(Argument, parameter)매개변수
- 메소드 호출시 전달할 값이 있을 경우 인자를 통해 전달


return문
- 메소드의 연산결과를 반환하기 위해 사용
- 객체 or 변수변환가능
- 값 변환시 제어도 같이 이동(즉, return문 이후 코드는 접근 불가 코드)
- 리턴형이 void가 아닌경우를 제외하곤 반드시 return문을 가져야 한다.
- 리턴값은 단일값만 가능
- 반드시 데이터 타입이 같거나 암시적으로 형변환이 가능한 데이터 반환


인자사용
- 값으로 전달(Call by Balue) : 입력 전용인자
- 참조로 전달(Call by Reference) : 입출력 겸용 인자
- 출력 파라미터(Output parameter): 출력 전용인자
- 가변 인자 리스트


값으로 전달
- 메소드 호출시 변수의 값을 복사해서 메소드로 전달
- 메소드 내에서 인자값이 변경될수 있다.(Side Effect)가 없다.
- 메소드 안의 값은 밖에서 확인 불가능
- 인자값은 호환가능(암시적 형변환 가능)


참조에 의한 전달
- 값을 전달하는 것이 아니라 변수의 메모리 주소를 전달
- ref키워드 사용
- 인자의 데이터형이 일치해야 한다(암시적으로 형변환 불가능)
- 참조 전달시 반드시 변수값 초기화


  public static void callrefTest()
  {
      int i = 3;
      refTest(ref i);
      Console.WriteLine("Main출력 : " + i);
  }


  public static void refTest(ref int i)
  {
      Console.WriteLine("Test출력"+i);
      i =100;  
      Console.WriteLine(i);
  }


출력파라미터
- 메소드 호출시 값을 넘겨줄 필요가 없을 경우
- 메커니즘은 ref와 유사
- ref처럼 변수를 초기화 시키지 않아도 된다.
- out키워드 사용


 public static void callTest()
  {
      int i = 3;
      int j = 10;
      Console.WriteLine("{0}, {1}",i, j);
      Test(out i, out j);
      Console.WriteLine("{0}, {1}",i, j);

  }


 public static void Test(out int i, out int j)
  {

      //out으로 참조한 변수에는 값이 할당되지 않은상태이다.

      //때문에 변수값이 한번이상 변경 되지 않는 상태에서 사용하려 하면 오류발생

      i =100;
      j =200;
      Console.WriteLine("{0}, {1}",i, j);
  }


인자사용 가이드 라인
- value > out > ref
- 값으로 인자를 전달하는 경우가 일반적
- 함수가 반환할 값이 하나일 경우 value
- 함수가 반환할 값이 둘 이상일 경우 ref, out
- 인자가 함수에 대한 입력과 출력 두가지 목적으로 사용될때 ref


가변길이 파라미터
- 여러 개의 파라미터를 전달 할 경우
- Call by Value 형식이다.
- 메소드명(params int[] x)
- 인자 앞에 params키워드를 붙여 주면 가변인자됨. 배열 처럼 사용.
- 가변인자와 일반인자를 같이 사용할 경우 : 예) 메소드명(string str, params int[] x)
   -> 반드시 가변인자를 마지막에 명시


using System;
class Class1
{

   public static int Method1(params int[] x)
   { 
       int z=0;
       for(int i = 0; i < x.Length; i++)
       {
          z += x[i];
       }
       return z;
    }
  

   public static void Main()
   {
      int y = Method1(10,15,20,35);     

      Console.WriteLine("y의 값은 {0}입니다.",y);
    }
}


재귀메소드(Recursive Method)
- 자기자신을 호출하는 메소드
- 무한 루프 주의


  public static void callMethod()
  {
      long factNumber = 0;
      long number = 4;

      factNumber = Factorial(number);
      Console.WriteLine("{0}의 팩토리얼 값은 {1}입니다.", number, factNumber);

  }


  public static long Factorial(long number)
  {
      if(number>1)
         return number*Factorial(number-1);
      else
         return 1;
  }


 

메소드 오버로딩(Method Overloading)
- 한 클래스 내에서 같은 이름을 가지지만 다른 동작을 하는 메소드들
- Meshod Signature
- 다양한 종류의 파라미터를 갖는 동일한 이름의 메소드 선언 방식
- 파라미터 형식을 기반으로 내부적으로 별개의 메소드로 인식
- 실제 호출이 되는 메소드는 인자리스트에 의해서 결정(메소드 서명)
- 메소드 서명이 될수 있는 요소 : 메소드명,  인자의 타입, 인자의 갯수, out, ref, params키워드
- 메소드 서명이 될 수 없는 요소 : 인자의 이름, 리턴 타입


 1. public static void WriteLine();
 2. public static void WriteLine(int i);
 3. public static void WriteLine(int j);   // 2번과 동일한 메소드로 본다.
 4. public static void WriteLine(int i, int j);
 5. public static int WriteLine(int i);    // 2번과 동일하게 본다.
 6. public static void WriteLine(ref int i);
 7. public static void WriteLine(out int i);  //인자수가 같은경우 ref나 out는 둘중에 하나만 사용가능
 8. public static void WriteLine(string i);
 9. public static void WriteLine(out int i,  int j);  //ref나 out 둘다 사용시 인자수가 다르다면 가능


 메소드 오버로딩 가이드 라인
 - 서로 다른 인자 리스트를 가지는 비슷한 메소드를 정의할 경우(이름이 같다 -> 하는일이 비슷)
 - 기존의 코드에 새로운 기능을 추가하는 경우
 - 메소드 오버로딩의 되도록 자재 -> 디버깅, 유지보수 문제.

배열
- 같은 데이터형을 갖는 데이터 집합
- 하나의 변수명을 가지고 여러개의 동일한 데이터 형식을 포함
- 컬렉션(Collection)은 서로 다른 데이터를 묶는 집합
- Call by Reference


배열선언
- 배열은 System.Array를 상속받는 객체
- 인스턴스화를 해야 사용가능
- 자료형[차원] 배열명 = new 자료형[요소갯수]{초기화 리스트};
- 첨자는 항상 0부터 시작


1. int [] result;
2. long result;
3. long result [3];   // X, result 와 [3] 자리가 바꼈고, 숫자도 들어가면 안됨.
4. [] long result;   // X, 자료형과 []가 바뀜
5. char[,] result;
6. int [,,];       // X, 배열명이 바뀜
7. float [10,2] result; // X, 숫자가 들어가면 안됨
8. result = {1,2,3,4,5}; // X, 형식이 틀림


배열선언 예제
int[] result;          //배열을 선언만 한 것. 메모리에 배열변수가 생성, 배열에 대한 요소의 메모리는 생성하지 않음.
int[] result={1,2,3,4,5};     //선언과 동시에 값을 할당
int[] result=new int[5];     //선언을 하고 배열을 생성, 크기는 브래킷안의 숫자 만큼의 요소가 생성
int[] result=new int[5] {1,2,3,4,5};  //이 방식을 줄여서 두 번째 처럼 사용가능


배열 인스턴스화
- 배열의 크기가 지정된다.
- Value Type배열은 자동초기화 (정수형->0, 실수형->0.0,  bool->false,  char->space)
- 초기화리스트 : 초기화 리스트의 수와 배열의 요소의 수가 일치


1차원 배열 인스턴스화 & 초기화
int [] nums = new int[3] {1,2,3};
int [] nums = new int[] {1,2,3};
int [] nums =  {1,2,3};


2차원 배열 인스턴스화 & 초기화
int [,] nums = new int [2,2] {{1,2},{3,4}};
int [,] nums = new int [,] {{1,2},{3,4}};
int [,] nums = {{1,2},{3,4}};


배열의 크기
- 1차원 배열 : 배열명.Length
- 2차원 배열 : GetLength


public static void ArraySeek()
{
   //1차원 배열탐색
   int[] row = new int[10] {1,2,3,4,5,6,7,8,9,10};
  
   //for문
   for(int i=0;i<row.Length;i++)
       Console.Write("{0}\t", row[i]);

   Console.WriteLine();


   //foreach문
   foreach(int item in row)
       Console.Write("{0}\t", item);
   Console.WriteLine();


   //2차원 배열탐색
   int [,] table = new int[2,5] {{2,5,7,8,4},{9,3,6,4,2}};

   //int rank = table.Rank;//배열의 차원 : 2
   //int index = table.Length; // 값: 10, 전체배열크기값이 들어감, for문에서 Exception발생

   //            -> 올바른 예제 int index = table.Length/rank;
   //위처럼 반복할 횟수를 변수에 담아서 사용할수도 있지만 번거로움때문에 대부분 GetLength사용.


   //for문
   for(int i=0; i<table.GetLength(0); i++) //2차원배열에서의 1차원방의 갯수(행갯수)만큼 실행
       for(int j=0; j<table.GetLength(1); j++) //1차원배열의 방수(열갯수)만큼 실행
           Console.Write("{0}\t", table[i,j]);
   Console.WriteLine();


   //foreach문
   foreach(int item in table)
       Console.Write("{0}\t",item);
   Console.WriteLine();


   //배열을 생성하면 2차원이상도 실제로는 한줄에 모두 생성되므로 위처럼 실행하면 배열의 모든값들이 출력
   //배열의 차원과는 상관없이 배열값들만 필요하다고 생각될때 사용용이.
}


배열의 차원
- Rank : Array의 차수(차원의 수)를 가져옵니다.


불규칙 배열(Jagged Array)
- 하나의 배열 안에 여러 크기를 가지는 배열이 내장
- 배열선언 :  데이터형[ ][ ] 배열명 = new 데이터형[크기][ ];
- 사용의 필요성 : 배열의 배열을 선언해야 할 경우, 메모리를 효율적으로 관리해야 할 경우


public static void jaggedArray()
{
   //불규칙 배열 선언
   int[][] jaggedArray = new int[5][];

   jaggedArray[0] = new int[] {1,15};
   jaggedArray[1] = new int[] {3,5,7};
   jaggedArray[2] = new int[] {10,20,30,40,50};
   jaggedArray[3] = new int[] {80,100};
   jaggedArray[4] = new int[] {2,5,150,9};


     //불규칙 배열요소 접근
     for(int i=0; i<jaggedArray.Length;i++)
     {
        for(int j=0; j<jaggedArray[i].Length; j++)
           Console.Write("***");//jaggedArray[i],[j];
      Console.WriteLine();
      }

}


  //불규칙배열과 2차원 배열을 함께 사용

public class JaggedArrayTest{
     public static void Main(){
    
        int[ ][,] jagArray=new int[3][,];

        jagArray[0]=new int[2,2] {{3,4},{5,6}};
        jagArray[1]=new int[3,2];
        jagArray[1][0,0]=-1;
        jagArray[1][0,1]=-2;
        jagArray[1][1,0]=-3;
        jagArray[1][1,1]=-4;
        jagArray[1][2,0]=-5;
        jagArray[1][2,1]=-6;
        jagArray[2] = new int[3,3]{ {3,4,6}, {4,5,7}, {7,8,6}};


        for(int a=0; a<jagArray.Length; a++){ //3회 실행
            for(int b=0; b<jagArray[a].GetLength(0); b++){  //2차원배월의 행만큼 실행 : 2회
                for(int c=0; c<jagArray[a].GetLength(1); c++){ //2차원배열 각행별 컬럼 배열값 출력
                   Console.Write("jagArray["+a+"]["+b+","+c+"]:"+jagArray[a][b,c]+'\t');
                }
            Console.WriteLine();
            }
         Console.WriteLine();
        }


     }
  }


  *실행결과
  jagArray[0][0,0]:3      jagArray[0][0,1]:4
  jagArray[0][1,0]:5      jagArray[0][1,1]:6 

  jagArray[1][0,0]:-1     jagArray[1][0,1]:-2
  jagArray[1][1,0]:-3     jagArray[1][1,1]:-4
  jagArray[1][2,0]:-5     jagArray[1][2,1]:-6  

  jagArray[2][0,0]:3      jagArray[2][0,1]:4      jagArray[2][0,2]:6
  jagArray[2][1,0]:4      jagArray[2][1,1]:5      jagArray[2][1,2]:7
  jagArray[2][2,0]:7      jagArray[2][2,1]:8      jagArray[2][2,2]:6


배열복사(배열변수복사)
- 배열이 참조형 변수라 번지수를 공유


  int[ ] mydream  = new int[ ]{1,2,3,4,5,6};
  int[ ] myref1 = mydream;
  int[ ] myref2 = myref1;


  // 배열 변수들이 모두 하나의 메모리를 참조하고 있기 때문에 아무리 값을 할당해도 하나의 메모리를 그 대상으로 한다.
  //mydream  ==  myref1  ==   myref2  //참


배열비교 예제
int[] array_1=new int[5] {1,2,3,4,5};
int[] array_2;
array_2 = array_1;
if(arrray_1 == array_2);  //참


int[] array_1=new int[5] {1,2,3,4,5};
int[] array_2 = new int[5] {1,2,3,4,5};
if(array_1 == array_2); 

//거짓, new 키워드로 서로 다른 배열개체를 선언, 배열 요소들의 값은 같을 지언정 다른 개체가 되는 것이다.


if(array_1[0] == array_2[0]); //참, 값을 비교

Sort / Reverse
-  배열의 항목을 오름차순 or 내림차순으로 정렬
-  정적메소드
- System.Array.Sort(배열객체)
- System.Array.Reverse(배열객체)


 public static void ArrayMethod()
 {
      int[] row = new int[10] {23,45,1,4,98,22,6,16,23,48};

      Console.Write("Source : \t\t");
      ArrayPrint(row);


      //오름차순정렬
      Array.Sort(row);
      Console.Write("Sort : \t\t");
      ArrayPrint(row);


      //내림차순정렬
      Array.Reverse(row);
      Console.Write("Reverse : \t\t");
      ArrayPrint(row);

 }


 public static void ArrayPrint(int[] row)
 {
      for(int i=0;i<row.Length;i++)
      {
          Console.Write("{0,3}", row[i]);
      }
      Console.WriteLine();
 }


Clear
- 배열의 요소들을 0 or null로 초기화
- 배열자체는 남아있다.
- System.Array.Clear(배열명, 시작인덱스, 길이)


  int[] row = new int[10] {23,45,1,4,98,22,6,16,23,48};

  Console.Write("Source :\t");
  for(int i=0;i<row.Length;i++)
  {
   Console.Write("{0,3}", row[i]);
  }
  Console.WriteLine();


  //Clear
  Array.Clear(row,3,1); //3번째부터 1개 clear
  //Array.Clear(row,6,5); //6번째부터 5개 clear -> 인덱스가 배열을 넘어가므로 에러


  Console.Write("3-1 :\t\t");
  for(int i=0;i<row.Length;i++)
  {
   Console.Write("{0,3}", row[i]);
  }
  Console.WriteLine();


  Array.Clear(row,0,row.Length);
  Console.Write("0-Length :\t");
  for(int i=0;i<row.Length;i++)
  {
   Console.Write("{0,3}", row[i]);
  }
  Console.WriteLine();


Clone
- 배열의 복사본 생성(전체복사)
- 동일한 요소를 가지는 또 하나이 배열 객체 생성


        //배열복사
        public static void arrayClone()
        {
            int [] row = {0,1,2,3};
            int [] copyRow = (int[]) row.Clone();
            //원본을 복사해서 또하나의 다른 배열을 만든다. row와 copyRow는 별개.


            //int [] copyRow = row.Clone();
            //에러, Clone자체가 오브젝트로 반환을 시키기 때문에 형변환을 꼭 해주어야 한다.


            /*
             * int [] copyArray = row;

             * row배열의 참조변수를 복사하기 때문에 row와 copyArray는 같은 배열을 가르킴
             * copyArray[0] = 100;

             * 복사본의 값을 변경시키면 row와 copyArray의 배열값은 같이 변경됨

             */


            Console.Write("\nSource :\t");
            for(int i=0;i<row.Length;i++)
            {
                Console.Write("{0,3}", row[i]);
            }
            Console.WriteLine();


            Console.Write("\nCopy :\t");
            for(int i=0;i<copyRow.Length;i++)
            {
                Console.Write("{0,3}", copyRow[i]);
            }
            Console.WriteLine();

        }


GetLength : 해당차원의 길이 변환
GetLowerBound : 해당차원의 시작첨자 반환
GetUpperBound : 해당차원의 끝 첨자 반환


   int [] row = {0,1,2,3};
   Console.WriteLine("GetLength : {0}", row.GetLength(0));      // 4
   Console.WriteLine("GetLowerUbound : {0}", row.GetLowerBound(0)); // 0
   Console.WriteLine("GetUpperUbound : {0}", row.GetUpperBound(0));  // 3


IndexOf / LastIndexOf
- 배열에서 지정된 값을 검색, 위치값을 돌려준다.
- 검색된 값이 없거나 실패하면 -1리턴
- IndexOf(배열명, 검색인자)
- LastIndexOf(배열명, 검색인자)


         public static void arrayIndexOf()
        {
            //배열안에서 특정위치값 찾기
            int[] row = new int[10] {23,45,1,4,32,22,6,32,23,48};

            int firstSearch, lastSearch;
            firstSearch =  Array.IndexOf(row, 32);          //배열의 앞에서 부터 찾는다.  결과 : 4
            lastSearch = Array.LastIndexOf(row, 32);     //배열의 뒤에서 부터 찾는다.  결과 : 7

            Console.Write("Source : ");
            for(int i=0;i<row.Length;i++)
            {
                Console.Write("{0,3}", row[i]);
            }


            Console.WriteLine();
            Console.WriteLine("IndexOf : 32의 위치값은 {0}", firstSearch);
            Console.WriteLine("LastIndexOf : 32의 위치값은 {0}", lastSearch);

        }


메소드에서의 배열사용
- 배열의 입력 인자로 사용
   참조형이라 Call By Reference로 적용
- 메소드의 반환값으로 사용
   하나이상의 값을 반환시...


        public static void MethodArrary()
        {
            int [] row = {1,2,3,4,5};

            Console.WriteLine("Main내 메소드 실행전 : " + row[0]);
            callArrayMethod(row);
            Console.WriteLine("Main내 메소드 실행후 : " + row[0]);

            Console.WriteLine("--------------------------------------");

            Console.WriteLine("Main내 메소드 실행전 : " + row[0]);
            callArrayMethod((int[])row.Clone());
            Console.WriteLine("Main내 메소드 실행후 : " + row[0]);

            Console.WriteLine("--------------------------------------");

            callArrayMethod(new int[4] {2,3,4,5});

        }

        public static void callArrayMethod(int[] row2)
        {
            row2[0]++;
            Console.WriteLine("배열의 Clone하여 메소드로 넘겼을때 메소드 내 : " + row2[0]);

        }


커맨드 라인 입력 인자
- 콘솔에서 실행파일 다음에 인자전달(Main에게...)
- 보통옵션값으로 사용한다.

- static void Main(string[] args){
               for(int i=0; i<args.Length;i++)
                {
                        Console.WriteLine(args[i]);
                }
        }


  ex)  c:\csc Test.cs
 

배열예외처리
- 보통 배열첨자 범위를 벗아난 예외발생
- IndexOutOfRangeException

문자열
- 모든 문자열은 System.String로부터 상속
- 닷넷에서 string객체는 한번 인스턴스화가 되면 불편 -> StringBuilder사용


string선언
- 다른 참조형과 다르게 new연산자 이용X
- 모든 string형은 유니코드


문자열 길이
- 객체명.Length
- 읽기전용


문자열의 각 요소 접근하기
- 인덱서(indexer) :  문자열을 배열처럼 취급
- 읽기 전용


문자열의 추가
- Insert : 지정위치에 문자열추가
- Concat. + : 문자열합치기
- 자기자신에게 영향X


대소문자 변환
- ToLower : 소문자로 치환
- ToUpper : 대문자로 치환


공백 문자열 지우기
- Trim, TrimStart, TrimEnd
- 공백삭제


문자열치환
- Replace
- 문자열 내에 원하는 문자열을 바꾸고자 하는 문자열로 치환


문자열 검색, 추출
- IndexOf, LastIndexOf :

  문자열 검색,  찾고자하는 문자열의 첫번째 글자 위치값 리턴 (0부터시작)
- Substring : 문자열추출


문자열 포맷지정
- Console.WriteLine()에서 사용한 양식 문자열과 동일
- String.Format();


문자열 분할
- Split
- 구분자를 중심으로 문자열을 분할, 배열 형태로 반환


Equals
- 두문자열이 같은지 비교


지정문자열 삭제
- Remove


        public static void stringMethod()
        {

            //문자열 비교
            string str1="ABC";
            string str2="ABC";

            if(str1.Equals(str2))
                Console.WriteLine("동일");
            else
                Console.WriteLine("다름");


            //문자열삭제
            string str3 = "나는 문자열입니다.";
            Console.WriteLine(str3.Remove(3,4));    //3위체에서부터 4개 삭제


            //양식문자열
            str = String.Format("{0,10}","ABC");

           //콘솔이 아닌 웹등에서 사용용이, 사용법은 WriteLine과 같음.
            Console.WriteLine(str);                 //콘솔에서는 WriteLine 사용
            Console.WriteLine(String.Format("{0:f2}",1520));


            //문자열 일정문자단위로 분리
            string csv = "강남길, 최지우, 김희선,하늘이";
            string[] result;

            result = csv.Split(',');
            for(int i=0; i<result.Length;i++)
                Console.WriteLine(result[i].Trim());


            //문자열길이, 복제. 비교
            str = "가나다라마바사";
            Console.WriteLine("문자열 : {0}", str);
            Console.WriteLine("문자열 길이 : {0}", str.Length);
            Console.WriteLine("문자열 복제 : {0}", str.Clone());
            Console.WriteLine("문자열 비교 : {0}", str.Equals("ABCDE"));


            //Indexer
            char firstChar = str[0];
            Console.WriteLine("첫문자 : {0}", firstChar);       // 결과 : 가


            //문자열 추가
            str2="추가문자열";
            str = str.Insert(0, str2);  //0번째 위치에 str2문자열 추가
            Console.WriteLine(str);


            //문자열 합침
            str2 = String.Concat("가","마","사");
            Console.WriteLine(str2);
            str2 = String.Concat(str,"마","사");
            Console.WriteLine(str2);
            str2 = str2 + str;
            Console.WriteLine(str2);


            //대소문자 변환
            str = "Hi~ Hong~";
            Console.WriteLine(str.ToUpper());
            Console.WriteLine(str.ToLower());
            Console.WriteLine(str);


            //문자열 좌/우 공백제거
            str = " 잘 살아 보세   ";
            Console.WriteLine(">" + str.TrimStart() + "<") ;
            Console.WriteLine(">" + str.TrimEnd() + "<");
            Console.WriteLine(">" + str.Trim() + "<");
            Console.WriteLine(">" + str.TrimStart().TrimEnd() + "<");


            //문자열 치환
            str = "Hello~";
            Console.WriteLine(str);
            Console.WriteLine(str.Replace("~", "..."));


            //문자열 검색, 0부터 시작. 해당하는 문자열의 첫번째 위치값 리턴
            Console.WriteLine(str.IndexOf("녕하"));

            str = "I Love You~";


            //문자열 추출
            Console.WriteLine(str.Substring(2));    //2번째위치부터 끝까지
            Console.WriteLine(str.Substring(2,4));

        }


ToCharArray
- 문자열을 Char형의 배열 형식으로 반환합니다


        public static void ToCharArray_Test()
        {
            string str = "012wxyz789";

            char[] arr;

            arr = str.ToCharArray(3, 6);

           //3번째 위치에서 6개의 글자를 추출해 배열방에 각각 넣는다.
            Console.Write("The letters in '{0}' are: '", str);
            Console.Write(arr);             //배열방 출력시 해당내용 출력

            for(int i=0;i<arr.Length;i++)   //각각의 배열방에도 들어가 있음.
                Console.WriteLine(arr[i]);

            Console.WriteLine("'");


            char[] array;
            array = str.ToCharArray();      //인자를 써주지 않으면 모든 문자 배열에 집어넣음
            Console.WriteLine("Each letter in '{0}' is:", str);
            Console.WriteLine(array);

            foreach (char c in arr)
                Console.WriteLine(c);
        }


System.Text.StringBulider
- 닷넷에서 String클래스를 이용한 문자열 처리는 비효율적

- 대형 문자열이나 잦은 문자열 변경시
- 대부분의 문자열 관련 함수들이 String 인스턴스를 새로 생성시켜 반환

- 버퍼 크기 증가(필요한 경우)와 길이 추적을 자동으로 처리


    StringBulider sb = new StringBulider("ABC");


 - StringBuilder.Capacity : 객체내에 문자열 버퍼 크기
 - StringBuilder.Length : 문자열 길이


        public static void StringBuilderTest()
        {
            StringBuilder sb = new StringBuilder("I am a Boy", 18);
            //가용량을 적어주지 않으면 기본 용량은 16이고, 기본 최대 용량은 Int32.MaxValue

            Console.WriteLine(sb.ToString());
            Console.WriteLine(sb.Length);
            Console.WriteLine(sb.Capacity);
            Console.WriteLine(sb.MaxCapacity);

            Console.WriteLine(sb.Insert(0,"삽입"));

            Console.WriteLine(sb.Remove(0,2));

            Console.WriteLine(sb.Append("추가문자열"));
            Console.WriteLine(sb.Append(5000));
            Console.WriteLine(sb.AppendFormat("{0,10}","end"));

            Console.WriteLine(sb[0]);
            sb[0] = 'i';
            Console.WriteLine(sb[0]);

            Console.WriteLine(sb.ToString().Replace(" ",""));
            Console.WriteLine(sb.ToString().IndexOf("Boy"));

        }


ASPX Web Form과 같이 데이터 스트리밍을 지원하는 환경에 있거나 응용 프로그램에서 데이터를 디스크에 기록할 경우에는 연결의 버퍼 오버헤드나 StringBuilder 클래스를 사용하지 말고 Response.Write 메서드나 해당 스트림에 적합한 메서드를 통해 스트림에 직접 데이터를 기록하십시오.
 

StringBuilder 클래스를 필요할 때마다 다시 할당하지 말고 기존의 StringBuilder 클래스를 다시 사용해 보십시오. 그러면 불필요한 힙 증가를 억제하고 가비지 수집을 줄일 수 있습니다. 두 경우 모두 StringBuilder 클래스를 사용하면 + 연산자를 사용하는 것보다 효율적으로 힙을 사용할 수 있습니다.

객체지향
 - 절차 지향 프로그래밍 : 기능이나 동작 절차를 나열한 형태
 - 객체 지향 프로그래밍 : 기능 위주가 아닌 객체 위주의 프로그래밍, 이벤트 위주


 객체(Encapsulation)
 - 데이터 + 프로시저
 - 독자성(Indentity) : 객체는 다른 객체와 구분
 - 상태(State) :  객체의 상태(객체만의 데이터)
 - 행동(Behavior) : 객채의 행동(메소드, 프로시저)
 - 실세계 객체를 모델링해서 소프트웨어 객체 생성


 캡슐화(Encapsulation)
 - 내부 정보 은닉화
 - 외부에서 객체 내부의 데이터를 함부로 접근 불가
 - 객체 내부를 몰라도 객체 사용가능
 - 간결한 코드 작성 : 내부 아키텍처를 몰라도 공개된 인터페이스만 알면 코딩 가능
 - 모듈화 : 모든 내부 코드가 객체 내에 기술
 - 정보은닉 : 객체내 데이터는 공개된 메소드를 통해서만 제어 가능


 메시지(Message)
 - 객체끼리 주고 받는 명령 혹은 정보(통신)
 - 파라미터를 통해 출력결과를 다양하게 반환
 - 객체의 메소드
 - 모든 기능을 하나의 객체에 집중할 필요 없음 -> 특정 기능 필요시 그 객체와만 대화


 객체 & 클래스
 - 클래스는 객체에 대한 설계도


 클래스 & 인스턴스
 - 인스턴스는 클래스를 실제 구현한 객체
 - 인스턴스로 객체 생성시 다양한 객체를 생성(Indentity, State...)


 클래스 상속(Inheritance)
 - 슈퍼클래스(Super Class) :  자신을 상속하는 클래스(부모클래스, 기본클래스)
 - 서브클래스(Sub Class) :  상속을 받는 클래스(자식클래스, 파생클래스)
 - 서브클래스는 슈퍼클래스의 변수 및 메소드를 상속받는다
 - 상속 전의(Transition) :  부모의 메소드를 자식, 손자, 증손자에게 계속 전달


 다형성(Polymorphism)
 - 클래스는 다양한 형태를 가질 수 있다.
 - 오버라이딩(Overridion), 오버로딩(Overloadiong)
 - 중복정의 or 재정의를 통해서 구현


클래스(Class)


클래스멤버(Class Member)
- 클래스를 구성하는 요소
- 필드(Field) : 필드 or 멤버변수
- 메소드(Method) : 객체행동
- 프로퍼티(Property) :  특성, 속성,.. 밖에서 보기엔 멤버 변수지만, 내부적으로  메소드
- 상수(Constant) : 읽기전용
- 인덱서(Indexer) : 스마트 배열
- 이벤트(Event)
- 연산자(Operator) : 연산자 오버로딩


접근제한자(Access modifier)
- 클래스 멤버의 보안제어
- 캡슐화, 내부은닉화
- public, private, protected, internal


public
- 클래스 멤버를 공개하고자 할때..(인터페이스)
- 보통 데이터는 은닉화, 메소드는 공개
- 누구나 사용가능, 클래스 내부, 파생 클래스, 클래스 외부


private
- 멤버를 포함한 클래스 내부에서만 접근가능
- 가장 엄격한 수준의 접근 제한자
- 보통 데이터의 접근 제한자.


protected
- 클래스 내부와 파생클래스에서만 접근가능
- 클래스 자신과 파생 클래스(상속받은 클래스)에서만...
- 반드시 동일어셈블리는 아니다.


internal
- 동일 어셈블리내에서 접근가능
- 외부에서 참조할 일이 없을 경우(참조해선 안되는 경우)
- 내부에서만 사용해야 하는 경우


protected internal
- 동일 어셈블리안에 있는  파생 클래스에서만 가능


기본 접근 제한자
 - class : private
 - 멤버 : private
 - struct : private
 - enum : public
 - interface : public

정적(클래스) 변수 & 메소드

    public class Test
    {
        public int i;   //객체변수
        public static int j =0;

        public void IncreaseI()
        {
            i++;
        }


        public void IncreaseJ()
        {
            j++;
        }


        public static int num = 0;
        public Test()   //기본생성자
        {
            num++;
        }


        public static int mtTest()
        {
            return num;
        }
    }


    class MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            Test n1 = new Test();
            Test n2 = new Test();
            Test n3 = new Test();
            Test n4 = new Test();
            Test n5 = new Test();
            Console.WriteLine(Test.num);
            //num은 정적변수이기 때문에 객체를 생성될때마다 기본생성자가 실행되어 변경된 값을 그대로 적용받는다.


            Console.WriteLine("객체생성전 : "+Test.j);

            //static으로 선언하지 않았을경우에는 객체를 생성해서 사용한다.
            //똑같은 이름의 객체를 생성한다 하더라도 새로 생성한 두 객체는 기본틀만 같을 뿐 각각 다른 객체이다.


            Test t = new Test();
            t.i = 100;
            t.IncreaseI();
            t.IncreaseI();


            Test t2 = new Test();
            t2.i = 200;
            t2.IncreaseI();
            t2.IncreaseJ();


            Console.WriteLine("t.i = " + t.i);
            Console.WriteLine("t2.i = " + t2.i);

            Console.WriteLine("Test.j = " + Test.j);
            // static으로 선언한 객체는 클래스명.객체명으로만 접근가능하다.
            // 각 객체간의 공통된 데이터로 사용하고 싶을때 사용..
        }
    }


객체생성
- 클래스를 기반으로 객체를 생성
- 인스턴스화 or 객체생성
- new연산자 + 객체 생성자(Constructor)


this 연산자
- 객체 자신을 의미(메소드가 호출되는 객체자신)
- 정적 메소드에선 불가능(객체에서만 사용가능)
- this로 객체반환도 가능


    public class Goods
    {
        private int price, quantity;
        //private static int a;

        public Goods SetPrice(int price)
        {
            this.price = price;
            //this.a = price    //정적변수는 this로 접근할수 없다.
            return this;    //SetPrice메소드의 리턴형태는 Goods

            //위는 아래형태로 리턴하는것과 같다.
            //Goods goods = new Goods();

            //return Goods;
        }


        public Goods SetQuantity(int quantity)
        {
            this.quantity = quantity;
            return this;
        }


        public int PutPirce()
        {
            return this.price;
        }


        public int PutQuantity()
        {
            return this.quantity;
        }

    }


    public class callClass
    {
        public static void ReturnThis()
        {
            Goods product = new Goods();
            product.SetPrice(100).SetQuantity(30);
            //product.SetPrice(100);
            //product.SetQuantity(30);
            //위와 같은 형태
            //product.SetPrice(100)의 리턴값이 Goods 이기때문에
            //그다음에 실행해야 하는 문장은 product.SetQuantity(30);과 같다.

            Console.WriteLine("가격은 : {0,3}원", product.PutPirce());
            Console.WriteLine("갯수은 : {0,3}개", product.PutQuantity());
        }
    }

위임과 이벤트(Delegate &  Event)
- 메소드의 실행을 위임 변수가 대행
- 메소드 포인터
- 위임형식은 동일한 파라미터와 반환형을 가지는 여러개의 메소드를 수행 가능하다.
- 객체 메소드 or 정적메소드 둘다 가능
- 위임은 메소드 포인터만 저장, 코드 기술은 불가
- 인터페이스를 지원하는 객체들 내에서 동일한 메소드 명으로 객체 내의 메소드 수행이 가능하듯이 위임도 동일한 위임명으로 메소드 형식이 동일한 다른 메소드 실행이 가능(다형성)


위임선언
- public delegate 반환형 위임명(인자리스트);
- delegate 키워드(본문X)
- 메소드 파라미터 형식과 메소드 반환형식이 같으면 위임 형식에 위임 가능


publid delegate void Sample (int x, int y);
- public static void M1(int x, int y);         //가능
- public void M2(int x, int y)                  //가능
- public int M3(int x, int y);                   //X, 리턴타입이 다름
- public void M4(int x);                        //X, 인자갯수가 다름
- public void M5(string x, int y);          //X, 인자의 타입이 다름
- public void M6(int x, int y, int z);      //X, 인자수가 다름


위임생성
- System.Delegate와 System.MulticastDelegate로 부터 상속
- 따라서 new 연산자로 위임 객체 생성
- 생성자의 인자는 반드시 정적메소드면 "클래스명.메소드명", 객체메소드면 "객체명.메소드명"
   
    Sample d = new Sample(Class.M1);     //인자에 메소드명만 들어간다.
    Sample d = new Sample(obj.M2);


using System;

namespace CSharpStudy
{
    class MyClass
    {
        public int num = 0;
        public void Plus(int value)
        {
            this.num +=value;
        }
        public void Minus(int value)
        {
            this.num -=value;
        }
        public static void PrintHello(int value)
        {
            for(int i=0; i<value; i++)
                Console.WriteLine("Hello~");
        }
    }

    //Delagete선언
    public delegate void Sample(int value);

    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            MyClass c = new MyClass();
            Sample d =  new Sample(c.Plus);


            //위임
            d(10);
            Console.WriteLine(c.num);

            c.Plus(10);
            Console.WriteLine(c.num);

            d = new Sample(c.Minus);
            d(10);
            Console.WriteLine(c.num);

            c.Minus(10);
            Console.WriteLine(c.num);


            //정적메소드 위임
            d = new Sample(MyClass.PrintHello);
            d(5);
           
        }
    }
}


 

위임사용
- 위임 객체 사용은 일반메소드와 동일
- 위임 호출시 파라미터를 전달하면 실제 메소드에 파라미터 전달, 호출
- 동일한 위임객체로 어떤 메소드를 참조하느냐에 따라 호출변경(메소드 다형성)
- 위임자는 변경할 수 없으며 일단 만들어지면 위임자의 호출 목록은 바뀌지 않는다.
- 여러 개의 호출목록을 지정해 주려면 그 Delegate의 형식은 리턴 타입이 반드시 void 이어야 함.
        ->  매개변수중에 어느것도 out 이 지정되어선 안된다.


위임연산
- Combine(+), Remove(-)
- 위임 객체에 메소드 추가, 제거
- 위임 객체가 가지는 메소드 리스트(Invocation List)


Combine & Remove
- Combine과 Remove를 명시적으로 호출해서 리스트 관리 기능


위임간 비교연산
- Equals, ==, != 재정의
- Invocation List를 비교한다.


using System;
namespace CSharpStudy
{
    // Mult Delegate 사용에 관한 예제입니다.

    class MyClass
    {
        public int num = 0;
        public void Plus(int value)
        {
            this.num +=value;
        }
        public void Minus(int value)
        {
            this.num -=value;
        }
        public static void PrintHello(int value)
        {
            for(int i=0; i<value; i++)
                Console.WriteLine("Hello~");
        }
    }

    //delegate 선언
    public delegate void Sample(int value);


    // 다중 메소드 연산이 가능
    // 멀티 Delegate를 사용할 때 메서드는 반드시 void를 반환해야 함 -> 호출을 목적으로 하기때문
    // void를 반환하지 않으면 +=과 같은 연산자를 사용할수 없다.
    // 중복 메서드를 포함 가능
    //호출된 메서드가 예외를 throw하면 메서드는 실행을 멈추고 예외는 대리자의 호출자에게 전달.
    //호출 목록에 남아있는 메서드는 호출되지 않음. 호출자 안에서 예외를 catch해도 이 동작은 변하지 않습니다.


    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            //위임 메소드 리스트 관리
            MyClass c = new MyClass();
            Sample d;


            //Delegate Combine
//          d = (Sample)Delegate.Combine(new Sample(c.Plus), new Sample(c.Minus));
            d =  new Sample(c.Plus) + new Sample(c.Minus);      // +연산자 오버로딩됨
            d(10);                  //c.Plus와 c.Minus 메소드가 순서대로 실행됨. 결과값 : 0
            Console.WriteLine(c.num);


            //Delegate Remove
//          d = d - new Sample(c.Minus);    //c.Minus메소를 제거
            d -= (Sample) new Sample(c.Minus);          // -=연산자 오버로딩됨
            d(10);                  //c.Plus메소드만 실행. 결과값: 10
            Console.WriteLine(c.num);


            //Delegate Combine
            d += new Sample(MyClass.PrintHello);

           //MyClass.PrintHello 메소드가 추가, +=연산자 오버로딩됨
            d(5);   //c.Plus와 MyClass.PrintHello 메소드 실행, 오래등록된 메소드부터 실행
            Console.WriteLine(c.num);


            //Deleate 비교연산  -> 가지고 있는 메소드 목록(목록 순서, 목록갯수 포함)을 비교한다.
            Sample s1 = new Sample(c.Plus) + new Sample(c.Minus);
            Sample s2 = new Sample(c.Plus);


            if(s1 == s2)
                Console.WriteLine("s1과 s2가 같습니다");
            else
                Console.WriteLine("s1과 s2가 다릅니다");
            // 가지고 있는 목록이 다르므로 다르다는 내용 출력

            s2 += new Sample(c.Minus);

            if(s1 == s2)
                Console.WriteLine("s1과 s2가 같습니다");
            else
                Console.WriteLine("s1과 s2가 다릅니다");
            // 가지고 있는 목록이 같으므로 같다는 내용 출력

     
        }
    }
}


 

이벤트(Event)
- 동작을 수행하는 방식
- 출판자(Publisher) : 이벤트를 발생하며 특정 구독자에게 이벤트 발생을 알려준다.
- 구독자(Subscriber) : 특정 이벤트 발생 통보를 받고 출판자로부터 호출되어질 메소드를 등록한


객체
- 이벤트 핸들러(Event Handler) : 호출되어지는 메소드
- 구독자들이 출판자에게 자신의 이벤트 핸들러를 출판자의 이벤트 핸들러 목록에 등록한다.


이벤트 정의
- event 키워드
- 접근지정자 event Delegate명 이벤트명;
- 반드시 위임과 같이 정의한다.


이벤트 핸들러 추가 및 해제
- +=, -+로 이벤트 핸들러를 출판자 객체에게 등록 및 제거한다.
- 위임 등록과 동일
- 객체명.이벤트명 += new Delegate명(객체명.메소드명);


구독자들에게 이벤트 발생 알리기
- 위임에서 위임 객체를 실행하며 Invocation List안에 모든 메소드를 실행하듯이 이벤트 요청이 발생하면 이벤트안에 이벤트 핸들러가 있는지 조사하고 이벤트 핸들러를 호출한다.


이벤트 핸들러 생성
- 모든 형태가능 -> 단 위임 형식과 동일한 메소드 형식
- 보통 포준적인 이벤트 핸들러 형식
  public delegate void EventHandler(object sender, EventArgs args)


- object sender : 동일한 이벤트 핸들러가 다중의 출판자에게 등록이 가능하므로 sender를 가지고 호출자(출판자)를 구별
- EventArgs args : 이벤트 발생시 추가 정보 전달
- 모든 이벤트 핸들러는 이 위임형과 반드시 동일


using System;

namespace CSharpStudy
{
    class Button
    {
        public event ButtonClick Click; //이벤트생성, 접근지정자 event Delegate명 이벤트명;
        public string Text;

        public void Trigger()
        {
            ButtonClickEventArgs e = new ButtonClickEventArgs();
            e.i = 100;
            Console.WriteLine("Button이 클릭되었습니다");
            Click(this, e); //실제로 이벤트를 발생시키는 부분
        }
    }

    //delegate
    public delegate void ButtonClick(object sender, ButtonClickEventArgs e);

    //사용자 정의 EventArgs
    public class ButtonClickEventArgs : EventArgs
    {
        public int i;
    }

    class  MainClass
    {

        //이벤트 핸들러 선언
        public static void button1_Click(object sender, ButtonClickEventArgs e)
        {
            //object sender는 메시지가 어디서 발생하는지 발생된 곳의 참조값
            // ButtonClickEventArgs e는 이벤트의 데이터를 담고 있는 매개변수.
            //이 두가지의 매개변수는 바꿀 수 없으며 항상 위와 같은 규칙으로 사용해야 함.

            Button button = (Button) sender;
            Console.WriteLine("Button Text : " + button.Text);
            Console.WriteLine("EventArgs : " + e.i);
            Console.WriteLine("ButtonClick 이벤트가 발생해서 이벤트 핸들러가 호출되었습니다.");
        }

        public static void button1_Click2(object sender, ButtonClickEventArgs e)
        {
            Console.WriteLine("Click2~");
        }

        [STAThread]
        static void Main(string[] args)
        {
            Button btn1 = new Button();
            btn1.Text = "테스트 버튼";
            btn1.Click += new ButtonClick(button1_Click);

            //ButtonClick(위임자)에 호출메서드 등록
            btn1.Click += new ButtonClick(button1_Click2);

            btn1.Trigger();     //이벤트 발생 에뮬레이터

            Console.Read();     //실행화면 일시중지
        }
    }
}

프로퍼티(Property)
- 클래스가 가지는 속성, 특성
- 멤버 필드(내부데이터)를 보호하되 외부에서 제어를 해야 하는 경우...
- 내부적으로는 메소드의 형태를 가지고, 외부적으로는 멤버 변수의 형태를 가진다.
- Smart Field or Logical Field


프로퍼티의 정의
- 파라미터가 없는 메소드 형식
- get, set 접근자 사용(일종의 내부메소드)
- 한 클래스내에서 프로퍼티명과 멤버변수의 명이 같으면 X (프로퍼티는 첫글자 대문자)
- get : 데이터를 읽어올때 (외부에서 클래스내의 데이터를...) 내부에 retrun문 필요(getter)
- set : 데이터를 설정할때, 내부에 value 키워드 사용 (setter)


private string text;   
public string Text      //text는 필드이지만 마치 메소드인것처럼 body를 가진다.
  {
    get {return text;} 
    set{text = value;}
  }
 
프로퍼티 형식
- 읽기 및 쓰기 지원 프로퍼티 : get, set 모두 정의한 프로퍼티
- 읽기 전용 프로퍼티 : get만 정의
- 쓰기 전용 프로퍼티 : set만 정의
- 정적 프로퍼티 : 정적메소드처럼 클래스 단위로 접근
- 계산된 프로퍼티 : 연산등 임의의 계산된 값을 다루는 프로퍼티 생성 가능


프로퍼티 & 필드
- void 형식을 제외한 데이터 형식 정의 가능
- 접근 제한자 선언가능
- 정적(static) 프로퍼티 선언 가능
- 필드 사용과 유사
- 프로퍼티는 값에 의한 전달만 가능
- 프로퍼티는 필드와는 달리 실제 메모리상에서 값을 가지고 있지 않기 때문에 주소와 관련된 ref,out같은 메소드 매게변수를 쓸 수 없다.


using System;

namespace CSharpStudy
{
    class Pen
    {
        private string color;

        public Pen() : this("검정"){}       //기본생성자

        public Pen(string color)        //기본생성자 오버로딩
        {
            this.color = color;
        }
        public void TextWrite(string text)   //출력메소드
        {
            Console.WriteLine("{0} color : {1}", color, text);
        }


    //메소드를 이용해서 지역변수 필드에 특성등을 변경후 사용할때
        public void ChangeColor(string color)
        {  
            this.color = color;
        }
        public string GetColor()
        {
            return this.color;
        }


    //프로퍼티를 이용할때
        public string Color //파라미터(인자값)이 생략된 메소드형태
        {
            //멤버변수와 프로퍼티명은 같으면 안되므로 프로퍼티명은 멤버변수명의 첫글자를 대문자로 바꾸어 사용한다.
            get { return this.color;}
            set { this.color = value;}
        }
    }

    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            Pen bPen = new Pen();
            Pen rPen = new Pen("빨강");
            Pen yPen = new Pen("노랑");

            bPen.TextWrite("테스트");
            rPen.TextWrite("테스트");
            yPen.TextWrite("테스트");


            //메소드 이용
            Console.WriteLine("------------ Method 이용---------------");
            bPen.ChangeColor("보라");
            bPen.TextWrite("테스트");


            //프로퍼티를 이용
            Console.WriteLine("------------ Property이용---------------");
            bPen.Color = "보라";        //프로퍼티명에 값 입력
            bPen.TextWrite("테스트");

        }
    }
}


프로퍼티 & 메소드
- 반환 형식 설정
- virtual, abstract, override 사용가능
- 객체 내의 상태에 대한 접근 분리
- 프로퍼티는 void형식 불가
- 프로퍼티는 value 인자 하나외에는 파라미터 추가 불가


    abstract class A
    {
        int y;
        public virtual int X
        {
            //가상 읽기전용프로퍼티
            get { return 0; }
        }
        public virtual int Y
        {
            //가상 읽기,쓰기프로퍼티
                get { return y; }
                set { y = value; }
        }
        public abstract int Z
        {
            //추상 읽기,쓰기프로퍼티
            get; set;
        }
    }
    class B: A
    {
        int z;
        public override int X {   //오버라이드(가상) 프로퍼티
                get { return base.X + 1; }
        }
        public override int Y {   //오버라이드(가상) 프로퍼티
                set { base.Y = value < 0? 0: value; }
        }
        public override int Z {   //오버라이드(추상) 프로퍼티
                get { return z; }
                set { z = value; }
        }


 

프로퍼티 가이드 라인
- 객체에서 겉으로 드러나는 속성일 경우...
- 읽기전용 속성, 쓰기 전용 속성 생성자 ->* readonly를 이용한 읽기전용 필드보다는 get을 이용해서 많이 사용
- 값을 받아 들일때 값의 검증이 필요한 경우...


using System;

namespace CSharpStudy
{
    class Person
    {
        private string lastName ="";
        private string firstName="";
        private int birthYear = 0;
        private static int generation  = 0;


        //읽기, 쓰기 프로퍼티
        public string LastName
        {  
            get {return lastName;}
            set {this.lastName = value;}
        }


        //읽기전용 프로퍼티
        public string FirstName
        {  
            get {return "김";}
        }


        //쓰기전용프로퍼티
        public int BirthYear
        {
            set
            {
                //입력값 검증
                if(1950<=value && value <=2050)
                    this.birthYear = value;
                else
                    birthYear = 0;
            }
        }


        //계산된 프로퍼티
        public int Age
        {
            get{return 2004-birthYear;}
        }


        //정적프로퍼티 : 정적멤버를 사용하기 위해서... 정적메소드와 동일한 형식
        public static int Generation
        {
            get{return Person.generation;}
            set{Person.generation = value;}
        }
       
    }


    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            Person hong  = new Person();

            hong.LastName = "길동";
            //hong.FirstName = "홍";    //읽기전용이기 때문에 에러
            hong.BirthYear = 1970;

            Console.WriteLine(hong.FirstName + " " + hong.LastName);
            Console.WriteLine(hong.Age);


            Console.WriteLine("계산전 : "+Person.Generation);
            Person.Generation++;        //메소드인데 일반변수처럼 사용됨.
            Console.WriteLine("계산전 : "+Person.Generation);

        }
    }
}

인덱서(Indexer)
- 객체가 배열을 사용할때 객체 자체가 배열인 것 처럼 접근 가능
- Smart Array
- System.String


인덱서 선언
- 프로퍼티 선언과 유사
- this 키워드, 배열 첨자 추가
- get, set, value 사용
- 프로퍼티는 static일수도 있지만 인덱서는 반드시 인스턴스 멤버로만 존재 (static은 인덱서 선언에 사용불가)
- 인덱서는 프로퍼티와 마친가지로 실제값을 가지고 있지 않기 때문에 ref, out과 같은 접근자를 사용할수없다.


public string this [int index]
{
   get{return array[index];
   set {array[index]=value;}
}

- 반드시 첨자가 정수일 필요는 없다


using System;

namespace CSharpStudy
{
    class MyString
    {
        private string [] data; //인덱서를 이용해서 접근할 멤버 변수(배열)

        //배열 크기 초기화
        public MyString() : this(10) {} //기본생성자

        public MyString(int size)       //기본생성자 오버로딩
        {
            this.data = new string[size];
        }

        //인덱서
        public string this[int index]   //하나만 사용가능, 시그너처(차원)가 다를경우 오버로딩 가능
        {
            //다른필드처럼 인스턴스 뒤에 점연산자를 사용하지 않고

            //그 클래스 자체가 배열로 사용하기 때문에 this키워드가 인덱서의 이름으로 사용된다.

            get
            {
                //첨자 범위 체크
                if (index > -1 && index < data.Length)
                    return data[index];
                else
                {
                    return null; //예외처리부
                }
            }

            set
            {
                //첨자 범위 체크
                if(index >-1 && index<data.Length)
                    data[index] = value;
                else
                    throw new IndexOutOfRangeException();
                //일반적으로 try~catch의 예외처리는 해당메소드를 호출한곳에서 처리하도록 한다.
            }
        }

        public int Length   //배열의 크기는 한번정해지면 수정할수 없기 때문에 읽기전용만 가능
        {
            get {return data.Length;}
        }
    }

    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            MyString s = new MyString(5);

            //인덱서 호출 -> 객체명[인덱서]를 사용하기만 하면 인덱서가 호출된다.
            //인덱서 값 할당
            s[0]= "A";         
            s[1]= "BC";
            s[2]= "DEF";
            s[3]= "GHIJ";
            s[4]= "KLMNO";


            //인덱서 값 읽음
            for(int i=0; i<s.Length;i++)
                Console.WriteLine("s[{0}]={1}", i, s[i]);

        }
    }
}


 

인덱서 & 배열
- 인덱서는 모든 데이터 형식을 첨자로 사용가능
- 인덱서는 값에 의한 전달만 가능
- 인덱서는 오버로딩 가능
- 인덱서는 값의 추가 처리 가능


* 첨자를 문자열로 사용한 예제

using System;

namespace CSharpStudy
{
    class MyString
    {
        private string data1 = "ABCDE";
        private string data2 = "가나다라마";
        private int [] nums = new int[5];

        //Indexer
        public int this[int index]
        {
            get
            {
                return nums[index];
            }
            set
            {
                this.nums[index] = value;
            }
        }

        //indexer 오버로딩 가능
        public string this[string index]
        {
            get
            {
                //첨자 확인
                if (index == "ENGLISH")
                    return data1;
                else if (index == "KOREAN")
                    return data2;
                else
                    return null;
            }
            set
            {
                if (index == "ENGLISH")
                    this.data1 = value;
                else if (index == "KOREAN")
                    this.data2 = value;
                else
                    throw new IndexOutOfRangeException();
            }
        }
    }

    class MainClass
    {
        public static void Main()
        {
            MyString s = new MyString();

            s["ENGLISH"] = "I'm American";
            s["KOREAN"] = "나는 한국인입니다";

            Console.WriteLine("ByEnglish : {0}", s["ENGLISH"]);
            Console.WriteLine("ByKorean : {0}", s["KOREAN"]);

            s[0] = 100;
            Console.WriteLine(s[0]);
        }
    }
}


 

인덱서 & 프로퍼티
- 둘다 get, set사용
- void불가
- 인덱서만 오버로딩 가능(프로퍼티는 인자리스트X)
- 프로퍼티는 정적 구현 가능


파일 & 디렉토리
- System.IO
- FileInfo
- DirectoryInfo
- FileSystemInfo
- 파일 및 디렉토리 조작 관련 클래스


인코딩
- 문자 코드를 컴퓨터가 이해가능한 0과 1의 바이너리 값을 가지는 연속적인 비트형태로 매핑시켜주는 작업
- ASCII : 7비트사용, 총 128문자 표현
- ISO-8859-1 : 8비트 사용, 서유럽 문자 집합, 기존 ASCII코드에 추가 문자 포함, 총 256문자 표현
- KSC 5601 : 한국 공업표준, 2바이트 완성형 한글 표현, ASCII 문자제외
- EUC-KR : ASCII문자 코드는 1바이트, 한글은 2바이트로 표현
- 유니코드 : 인간이 사용하는 모든 언어표현, 2바이트 사용, 총 65365개 문자표현
- UTF-8 : ASCII문자 코드는 1바이트, 다른 문자는 2바이트나 그 이상으로 표현(한글은 3바이트로 인코딩), 기본 사용 추세
- UTF-16 : 2바이트로 모든 문자 코드 표현


스트림
- C#에서의 모든 입출력
- 스트림을 통해 입출력되는 단위는 byte -> 스트림 내부에는 데이터 타입X -> 어떤 종류의 입출력장치나 파일도 쉽게 처리
- 프로그램상의 객체(데이터)는 데이터 타입이 있기 때문에 이런 바이트 단위와 변한하는 과정이 필요 -> 닷넷 스트림 관련 클래스
- System.IO : 스트림 관련 클래스


Stream 추상클래스
- 모든 스트림
- FileStream, MemoryStream, NetworkStream
- 입출력 장치에 관계없는 일관된 프로그래밍 지원


FileStream
- 파일에 들어있는 데이터를 바이트 배열로 읽고 쓰기 위한 기능 제공


Stream 관련 클래스
- 의미없이 연속된 바이트 데이터를 의미있는 타입 데이터로 바꾸는 기능 제공
- Reader / Writer
- StreamReader : 파일등의 입출력 장치에서 얻은 바이트들을 여러 인코딩을 통해서 의미있는 문자나 문자열로 해석(TextReader 구현)
- StreamWriter : 프로그램에서의 의미있는 문자나 문자열을 입출력 장치로 쓸수 있게 한다(TextWriter 구현)
- 개발자는 더이상 바이트 스트림에 대한 신경 안써도 됨 -> 스트림을 직접 다루지 않아도 된다.


FileStream.Flush
- 스트림에 대한 모든 버퍼를 지우고 버퍼링된 모든 데이터가 내부 장치에 저장되도록 한다.
- Close를 하기전에 적절히 사용


파일 메소드

- 파일에 텍스트 추가 : File.AppendText, FileInfo.AppendText

- 파일이름 변경, 이동 : File.Move,FileInfo.MoveTo

- 파일삭제 : File.Delete, FileInfo.Delete

- 파일복사 : File.Copy, FileInfo.CopyTo

- 파일 크기를 정보 : FileInfo.Length 

- 파일특성 가져옴 : File.GetAttributes 

- 파일의 특성 설정 : File.SetAttributes 

- 파일이 존재여부 체크 : File.Exists 

- 파일의 정규화된 경로를 검색 : Path.GetFullPath

- 파일 확장명을 검색 : Path.GetExtension 

- 경로에서 파일 이름 및 확장명을 검색 : Path.GetFileName 

- 파일 확장명을 변경 : Path.ChangeExtension 


* 파일 속성 이용 예제

using System;
using System.IO;

namespace CSharpStudy
{
    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            string path = @"C:\boot.ini";


            Console.WriteLine("------------- -FileInfo ------------------");

            FileInfo file = new FileInfo(path);


            if(file.Exists)     //파일이 해당경로에 존재하는지 여부 체크
            {
                Console.WriteLine("Attributes : {0}", file.Attributes);
                Console.WriteLine("File Name : {0}", file.Name);
                Console.WriteLine("File Ext : {0}", file.Extension);
                Console.WriteLine("File Size : {0}", file.Length);
                Console.WriteLine("Create time : {0}", file.CreationTime);
                Console.WriteLine("DirectoryName : {0}", file.DirectoryName);
                Console.WriteLine("Full Name: {0}", file.FullName);
                Console.WriteLine("Last AccessTime : {0}", file.LastAccessTime.ToString());
                Console.WriteLine("Last WriteTime : {0}", file.LastWriteTime.ToString());
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다.");
            }


            Console.WriteLine("\n");

            Console.WriteLine("-------------- DirectoryInfo ------------------");

            string dirPath = @"D:\Inetpub\wwwroot";
            DirectoryInfo dir  = new DirectoryInfo(dirPath);


                Console.WriteLine("Attributes : {0}", dir.Attributes);
                Console.WriteLine("Directory Name : {0}", dir.Name);
                Console.WriteLine("Parent : {0}", dir.Parent.Name);
                Console.WriteLine("Root : {0}", dir.Root.Name);
                Console.WriteLine("Create time : {0}", dir.CreationTime);
                Console.WriteLine("Full Name: {0}", dir.FullName);
                Console.WriteLine("Last AccessTime : {0}", dir.LastAccessTime.ToString());
                Console.WriteLine("Last WriteTime : {0}", dir.LastWriteTime.ToString());

        }
    }
}


 

*파일 메소드 이용예제

using System;
using System.IO;

namespace CSharpStudy
{
    class  MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {
            string path = @"text.txt";
            FileInfo file = new FileInfo(path);
            bool flag = true;

            while(flag)
            {
                Console.WriteLine("=====================");
                Console.WriteLine(" 파일 처리");
                Console.WriteLine("=====================");
                Console.WriteLine("1. 파일생성");
                Console.WriteLine("2. 텍스트추가");
                Console.WriteLine("3. 파일 읽기");
                Console.WriteLine("4. 파일삭제");
                Console.WriteLine("5. 파일 복사");
                Console.WriteLine("6. 파일이동");
                Console.WriteLine("7. 종료");
                Console.Write("\n원하시는 작업 : ");

                string sel = Console.ReadLine();
                switch(sel)
                {
                    case "1":
                        CreateText(file);
                        break;
                    case "2":
                        AppendText(file);
                        break;
                    case "3":
                        ReadText(file);
                        break;
                    case "4":
                        DeleteFile(file);
                        break;
                    case "5":
                        CopyFile(file);
                        break;
                    case "6":
                        MoveFile(file);
                        break;
                    case "7":
                    default:
                        Console.WriteLine("프로그램을 종료합니다.");
                        flag = false;
                        break;
                }
            }
        }

        //파일생성
        public static void CreateText(FileInfo file)
        {
            StreamWriter writer = file.CreateText();
            //UTF-8로 인코딩된 텍스트를 쓰기 위해 파일을 만들거나 엽니다
            //file.CreateText(쓰기용으로 사용할 파일의 경로); -> 경로가 없으면  자기자신에게 실행

            writer.Close();
            Console.WriteLine("파일이 생성되었습니다\n\n");
        }


        //텍스트추가
        public static void AppendText(FileInfo file)
        {
            if(file.Exists)
            {
                Console.WriteLine("\n텍스트를 입력하세요(/p : 입력종료)");
                StreamWriter writer  = file.AppendText();
                //기존 파일에 UTF-8로 인코딩된 텍스트를 추가하는 StreamWriter를 만듬
                //file.AppendText(추가하고자하는 파일의 경로); ->경로가 없으면  자기자신에게 실행

                string inputString = Console.ReadLine();
                while(inputString != "/q")
                {  
                    writer.WriteLine(inputString);
                    inputString = Console.ReadLine();
                }

                writer.Close();
                Console.WriteLine("텍스트 추가가 완료 되었습니다.\n\n");
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다.");
            }
           
        }


        //파일읽기
        public static void ReadText(FileInfo file)
        {
            if(file.Exists)
            {  
                StreamReader reader = file.OpenText();

                //UTF-8로 인코딩된 기존 텍스트 파일을 읽기 용으로 엽니다

                Console.WriteLine(reader.ReadToEnd());
                Console.WriteLine("\n\n");
                reader.Close();
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다\n\n");
            }
        }


        //파일삭제
        public static void DeleteFile(FileInfo file)
        {
            if(file.Exists)
            {  
                file.Delete();
                Console.WriteLine("파일을 삭제하였습니다.");
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다\n\n");
            }
        }


        //파일복사
        public static void CopyFile(FileInfo file)
        {
            if(file.Exists)
            {  
                file.CopyTo("copy.txt", true);
                Console.WriteLine("파일을 복사하였습니다.");
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다\n\n");
            }
        }


        //파일이동
        public static void MoveFile(FileInfo file)
        {
            if(file.Exists)
            {  
                Console.Write("이동 경로 입력 : ");
                string dirPath = Console.ReadLine();
                file.MoveTo(dirPath + "\\move.txt");    //해당경로로 이름을 바꿔서 이동한다.
                Console.WriteLine("파일을 복사하였습니다.");
            }
            else
            {
                Console.WriteLine("파일이 존재하지 않습니다\n\n");
            }
        }

    }
}

using System;

namespace cSharpStudy
{
    class MainClass
    {
        [STAThread]
        static void Main(string[] args)
        {  

            DateTime myDate = DateTime.Now;
            Console.WriteLine(myDate.ToString());

            Console.WriteLine("DateTime.Now : "+DateTime.Now);

            // 현재 날짜및 시간


            Console.WriteLine("DateTime.Today : "+DateTime.Today);
            //0시 0분 0초로 셋팅된 현재 날짜와 시간


            Console.WriteLine("myDate.Year : "+ myDate.Year);   //현재날짜의 년도, int
            Console.WriteLine("myDate.Month : "+ myDate.Month);   //현재날짜의 월, int
            Console.WriteLine("myDate.Day : "+ myDate.Day);      //현재날짜의 일, int
            Console.WriteLine("myDate.Hour : "+ myDate.Hour);
            Console.WriteLine("myDate.Minute : "+ myDate.Minute);
            Console.WriteLine("myDate.Second : "+ myDate.Second);
            Console.WriteLine("myDate.Millisecond : "+ myDate.Millisecond);
            Console.WriteLine("myDate.DayOfWeek  : "+ myDate.DayOfWeek );  
            Console.WriteLine("myDate.DayOfYear  : "+ myDate.DayOfYear );


            Console.WriteLine("myDate.Date : "+ myDate.Date);
            Console.WriteLine("myDate.Ticks : "+ myDate.Ticks);

            Console.WriteLine("DateTime.UtcNow : "+ DateTime.UtcNow);
            //UTC Universal Time Coordinated 협정 세계시(時)
            //협정 세계시는 국제 사회가 사용하는 과학적 시간의 표준


            DateTime dt =  DateTime.Today;
            Console.WriteLine("오늘날짜 : " + dt);

            //지정한 년도가 윤년인지 아닌지를 체크
            Console.WriteLine(DateTime.IsLeapYear(dt.Year));        //결과값 : true or false


            // TimeSpan duration = new System.TimeSpan(1, 2, 3, 4);
            // Console.WriteLine(dt.Add(duration));                       
            Console.WriteLine("오늘날짜 + 1일 2시간 3분 4초 : " + dt.Add(new TimeSpan(1,2,3,4)));
            //new TimeSpan(day,hour,minute,second)
           
            Console.WriteLine(dt.AddDays(36));      //지정된 일수를 더한다.
            Console.WriteLine(dt.AddHours(20)); //지정된 시간값을 더한다.

            // AddMilliseconds :  지정된 밀리초수를 더한다.
            // AddMinutes : 지정된 분수를 더한다.
            // AddMonths : 지정된 월수를 더한다.
            // AddSeconds : 지정된 초수를 더한다.
            // AddYears : 지정된 연도수를 더한다.


            Console.WriteLine("\n\n");
            DateTime t1 = new DateTime(2004, 5, 10);
            DateTime t2 = new DateTime(2002, 10, 5);
            Console.WriteLine("{0}과 {1}를 비교합니다.: ", t1, t2);
            Console.WriteLine("Compare를 이용한 결과값 : " + DateTime.Compare(t1, t2));
            Console.WriteLine("CompareTo를 이용한 결과값 : " + t1.CompareTo(t2));
            // 0보다 작으면 : t1 < t2
            // 0과 같으면 : t1 = t2
            // 0보다 크면 : t1 > t2 or null


            Console.WriteLine(DateTime.DaysInMonth(2004, 5));
            //지정된 연도, 지정된 월의 일 수 반환


            DateTime july28 = new DateTime(1979, 7, 28, 5, 23, 15, 16);
            string[] july28Formats = july28.GetDateTimeFormats();

            //foreach (string format in july28Formats) {
            //  System.Console.WriteLine(format);
            //}


            Console.WriteLine(july28.ToString("d"));        // 1979-07-28
            Console.WriteLine(july28.ToString("D"));        // 1979년 7월 28일 토요일
            Console.WriteLine(july28.ToString("f"));        // 1979년 7월 28일 토요일 오전 5:23
            Console.WriteLine(july28.ToString("F"));        // 1979년 7월 28일 토요일 오전 5:23:15
            Console.WriteLine(july28.ToString("g"));        // 1979-07-28 오전 5:23
            Console.WriteLine(july28.ToString("G"));        // 1979-07-28 오전 5:23:15
            Console.WriteLine(july28.ToString("m"));       // 7월 28일
            Console.WriteLine(july28.ToString("r"));        // Sat, 28 Jul 1979 05:23:15 GMT
            Console.WriteLine(july28.ToString("s"));        // 1979-07-28T05:23:15
            Console.WriteLine(july28.ToString("t"));        // 오전 5:23
            Console.WriteLine(july28.ToString("T"));        // 오전 5:23:15
            Console.WriteLine(july28.ToString("u"));        // 1979-07-28 05:23:15Z  //세계 표준시
            Console.WriteLine(july28.ToString("U"));        // 1979년 7월 27일 금요일 오후 8:23:15
            Console.WriteLine(july28.ToString("y"));        // 1979년 7월
           
        }
    }
}

'.net' 카테고리의 다른 글

DB를 연결  (0) 2007.07.17
ADO.NET  (2) 2007.07.17
C# 정리 내용  (5) 2007.07.10
HttpRequest.ServerVariables Property  (2) 2007.07.04
Silverlight 개발자 100만명  (0) 2007.06.11
C# 정리 내용 (ADO.NET)  (0) 2007.05.03
  1. kpbjggvb 2007.08.09 20:53

    지금 바로 만나서 조건만남할 오빠만 연락주세요. KoreaSwapping.com 여기 오셔서 yasuni 찾으시면 되요. 장난사절, 매너남만 환영. 몸매,얼굴 자신있는 무용과 출신입니당~ KoreaSwapping.com

  2. nlxysaxs 2007.08.10 12:41

    지금 바로 만나서 조건만남할 오빠만 연락주세요. KoReaSwapping.com 여기 오셔서 yasuni 찾으시면 되요. 장난사절, 매너남만 환영. 몸매,얼굴 자신있는 무용과 출신입니당~
    KoReaSwapping.com

  3. kspbxnlt 2007.08.17 05:53

    검색만 하다가 지치는 미팅사이트 때려잡기!
    화상채팅에서 욕구충족이 불가능한 남성분 대환영!
    조건녀도 대환영!
    만나서 바로 엔조이 하는곳!
    KоreaSwapping.com

  4. ebony teen sex video 2008.03.13 06:08

    너는 아름다운 웹사이트가 있는다!

  5. babysitter pics xxx 2008.05.23 05:13

    일! 우수한 감사!

+ Recent posts