728x90


using System;
using System.Data.SqlClient;
 
namespace CS_ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnection con = new SqlConnection();
            //SqlCommand cmd = new SqlCommand();
            Console.WriteLine("연결시도");
 
            con.ConnectionString = "server=210.119.12.79,1617; database=ADO; uid=sa; pwd=it17";
 
            con.Open();
            SqlCommand cmd = new SqlCommand("SELECT * FROM ADO", con);
 
            Console.WriteLine("DB 연결 성공");
 
            /*
            Console.WriteLine("테이블 만들기 시도");
            cmd.Connection = con;
            cmd.CommandText = "CREATE table 성적표 (" +
                "번호 int primary key," +
                "이름 varchar(6) not null," +
                "국어 int," +
                "영어 int," +
                "수학 int)";
            
 
            cmd.ExecuteNonQuery();
            Console.WriteLine("테이블 만들기 성공");
            */
 
            Console.WriteLine("데이터 추가 시도");
            cmd.CommandText = "INSERT INTO 성적표 VALUES(1, '추경호', 95, 100, 85)";
            cmd.ExecuteNonQuery();
 
            cmd.CommandText = "INSERT INTO 성적표 VALUES(2, '조수현', 100, 80, 85)";
            cmd.ExecuteNonQuery();
            con.Close();
        }
    }
}



using System;
using System.Data.SqlClient;
 
namespace CS_ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnection con = new SqlConnection();
            Console.WriteLine("DB 연결시도");
            con.ConnectionString = "server=210.119.12.79,1617; database=ADO; uid=sa; pwd=it4321";
            con.Open();
            Console.WriteLine("DB 연결 성공");
 
            SqlCommand cmd = new SqlCommand(); 
            
            cmd.Connection = con;
 
            Display(cmd);
 
            cmd.CommandText = "update 성적표 set 영어=@영어,수학=@수학 " +
                "where 이름='조수현'";
            cmd.Parameters.AddWithValue("@영어"99);
            cmd.Parameters.AddWithValue("@수학"88);
            cmd.ExecuteNonQuery();
            Display(cmd);
 
            cmd.CommandText = "DELETE FROM 성적표 WHERE 이름 = @이름";
            cmd.Parameters.AddWithValue("@이름""조수현");
            cmd.ExecuteNonQuery();
            Display(cmd);
 
            con.Close();
        }
 
        static void Display(SqlCommand cmd)
        {
            SqlDataReader sqlReader = null;
            cmd.CommandText = "select * from 성적표";
            sqlReader = cmd.ExecuteReader();
 
            for (int i = 0; i < sqlReader.FieldCount; i++)
            {
                Console.Write("{0,7}", sqlReader.GetName(i));
            }
            Console.WriteLine();
 
            string field = "";
            while (sqlReader.Read())
            {
                for (int i = 0; i < sqlReader.FieldCount; i++)
                {
                    field = sqlReader.GetName(i);
                    Console.Write("{0,8}", sqlReader[field]);
                }
                Console.WriteLine();
            }
            sqlReader.Close();
        }
    }
}



윈폼


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace DB3
{
    public partial class Form1 : Form
    {
        SqlConnection con;
        SqlDataAdapter mSqlDataAdapter;
        DataSet mDataSet;
        DataTable mDataTable;
 
        public Form1()
        {
            InitializeComponent();
 
            try
            {
                if (con == null)
                    con = new SqlConnection();
 
                textBox1.AppendText("DB연결 시도\r\n");
                con.ConnectionString =
                "server=PKNU1\\SQLEXPRESS;database=ADO;uid=sa;pwd=1234";
                con.Open();
                textBox1.AppendText("DB연결 성공\r\n");
            }
            catch { textBox1.AppendText("DB연결 실패\r\n"); }
            updateDataView();
        }
 
        public void updateDataView()
        {
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = con;
            cmd.CommandText = "select * from 성적표";
 
            mSqlDataAdapter = new SqlDataAdapter();
            mSqlDataAdapter.SelectCommand = cmd;
 
            mDataSet = new DataSet("성적관리");
            mSqlDataAdapter.Fill(mDataSet, "성적표");
 
            mDataTable = mDataSet.Tables["성적표"];
            dataGridView1.DataSource = mDataTable;
        }
    }
}




'Study > C#' 카테고리의 다른 글

IP 주소 정수로 변환  (0) 2017.05.16
C# Thread  (0) 2017.04.24
C# 네트워크 기본  (1) 2017.04.12
C# 예외처리, delegate  (0) 2017.04.10
C# 인터페이스  (0) 2017.04.07
728x90


C# 네트워크 프로그래밍


참고 도서 : 가볍게 시작하는 리얼 C# 프로그래밍


실 세계의 집주소는 네트워크에서는 IP 주소에 비유할 수 있다.

192.168.0.32


192는 가장 큰 범위 주소, 뒤로 갈수록 작은 범위의 주소이다.

ip주소와 Port 번호를 이용해 연결하며, Port 번호는 이름이라고 할 수도 있을 것이다.


TCP

TCP는 연결 지항형 서비스로, 데이터 전송 전에 Hand Shake를 통해 상대방과 연결을 형성한다.



UPD

UDP는 Hand Shake가 필요하지 않으며, 비연결형 서비스이므로 상대방과 1:1 회선이 형성되지 않는다. 

패킷에 상대방의 주소를 입력해 네트워크를 전송하며, 이는 편지에 비유할 수 있다. 통화는 바로 서로간 연결을 하지만, 편지는 써서 우체통에 넣을 뿐이니 말이다.



TCP의 혼잡 제어(Congestion Control)

특정 TCP 연결이 과도한 양의 데이터를 전송하려하면, 송신측의 TCP가 전송하려는 양의 데이터를 조절해서 (통신하는 호스트들 사이에)
네트워크 자원이 폭주되는 것을 방지한다.


TCP의 신뢰적 데이터 전달(Reliable data transfer)

TCP는 데이터가 순서대로, 정확하게 전달되는 것을 보장한다. TCP 연결이 이렇듯 많은 기능을 지원하지만, 

UPD에 비해선 연결이 무겁고 데이터 전송에 더 큰 비용이 든다. (흐름 제어, 순서번호, 확인 응답 등의 메커니즘을 사용하므로)


이런 이유로 UDP는 화면 공유 등에 주로 이용되고, TCP는 채팅 프로그램에 많이 사용된다.






내 PC의 IP 주소 출력하기

using System;
using System.Net;
 
 
namespace Network
{
    class Program
    {
        static void Main(string[] args)
        {
            string hostName = Dns.GetHostName();
            Console.WriteLine(hostName);
 
            IPHostEntry host = Dns.GetHostEntry(hostName);
            Console.WriteLine(host.AddressList[1].ToString());
 
            for (int i = 0; i < host.AddressList.Length; i++)
            {
                string myIPAddress = host.AddressList[i].ToString();
                Console.WriteLine(myIPAddress);
            }
        }
    }
}



네트워크 프로그래밍의 여러 클래스들


IPAddress 클래스


 도트 4자리 표기법의 IP 주소 예) 192.168.210.21


IPAddress 객체의 초기화 

IPAddress ipAddress = IPAddress.Parse("192.168.210.21");

IPAddress(Int64) // Int64로 지정된 주소 사용


.Parse // IP 주소 문자열을 IPAddress 인스턴스로 변환



IPEndPoint 클래스

하나의 컴퓨터에는 여러 프로그램이 동시 동작하므로, 원하는 프로그램을 찾기위해 포트 번호가 필요하다.

IPEndPoint는 특정 컴퓨터의 프로그램을 가리키는 클래스이다.


IPAddress ipAddress = IPAddress.Parse("192.168.210.21");
            IPEndPoint ipep = new IPEndPoint(ipAddress, 9999);

소켓 클래스

소켓 클래스는 실제 서버와 클라이언트를 구축하기 위해 필요한 인터페이스이다. 

소켓은 서로 다른 OS간에 네트워크 통신을 가능하게 해주며, 네트워크 전문 지식이 없이도 프로그래밍이 가능하게 한다.


이 과정을 크게 잡으면 다음과 같다.

1. 서버에서 소켓 생성 및 서버 설정 후 접속 대기

2. 접속이 일어나면 데이터 전송이 발생하고 소켓을 닫음



서버의 구현


이미지나 동영상 등의 전송을 위해선 byte로 데이터를 주고 받아야 한다.

using System; using System.Text; using System.Net; using System.Net.Sockets; namespace TCP_Server {     class Program     {         static void Main(string[] args)         {             Socket server = null;             Socket client = null;             byte[] data = new byte[1024];             // 3317포트에 대해선 모든 형태의 ip에 접속 허가             IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9999);             server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);             server.Bind(ipep);  // ipep를 통해 서버 소켓과 결합             server.Listen(10);  // 최대 접속 가능한 클라이언트의 수를 지정 후, 대기             Console.WriteLine("서버 시작 \n 클라이언트 접속 대기.");             // 클라이언트 접속 대기, 동기화 상태 진입. 접속이 일어나면 클라이언트 소켓이 반환             client = server.Accept();             Console.WriteLine("클라이언트 접속 완료.");             // 접속한 클라이언트로부터 데이터 수신, DATA 변수 BYTE 배열 형태로 저장             client.Receive(data);             Console.WriteLine("클라이언트로부터 데이터 수신. \n 메시지 : "                 + Encoding.Default.GetString(data));             // 클라이언트와 서버 소켓을 각각 닫는다.             client.Close();             server.Close();         }     } }



클라이언트의 구현

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
 
namespace TCP_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[1024];
 
            IPAddress ipAddress = IPAddress.Parse("210.119.12.79"); // 접속할 서버의 IP 주소            
            IPEndPoint ipep = new IPEndPoint(ipAddress, 9999); // 1433은 port번호, 특정 애플리케이션을 지정
 
            // 서버 소켓 생성
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
                ProtocolType.Tcp);
 
            Console.WriteLine("서버 접속...");
 
            // 서버 접속
            server.Connect(ipep);
            Console.WriteLine("서버 접속 완료.");
 
            data = Encoding.Default.GetBytes("클라이언트에서 보내는 메시지");
            server.Send(data);
            Console.WriteLine("서버에 데이터 전송");
 
            server.Close();
        }
    }
}


비동기화를 위한 thread 적용

동기화는 어떤 입력이 들어올 때까지 프로그램의 제어가 멈추어져 있으므로, 네트워크 접속에서는 비효율적이고 불편함을 초래한다.

반면에, 비동기화는 작업을 요청한 뒤 결과의 수신 여부와 상관없이 다음 작업을 곧바로 진행하므로 실시간 처리와 다중 접속 형태에 알맞다.


비동기화를 위해 thread를 이용할 수 있으며, 하나의 프로그램을 두 개 이상의 흐름으로 나누어 병렬로 실행할 수 있게 된다.

(하나의 thread는 Accept() 메소드를 실행하고, 기존의 프로그램 제어는 다른 thread가 담당)



커널 버퍼

https://colorscripter.com/s/dbnDSsE

위의 소스에서 Receive() 메소드는 상대에게서 바로 데이터를 받아오는 것이 아니다. 


Receive() 메소드를 호출하기 전에 이미 커널 버퍼에 데이터는 수신되어 있고, Receive() 메소드를 호출함으로써 가져오는 것이다.

이는 자원을 효율적으로 가져오기 위함이지만 문제가 있다. 


몇 번의 데이터를 수신하였는지 알 수 없고,  얼마 만큼 데이터를 한번에 처리해야 할지 알수 없는 것이다. 

이를 해결하기 위해서 패킷을 정의할 필요가 있다.



패킷의 정의

패킷은 네트워크 전송에 있어서 하나의 단위이다.


보통 TCP 통신을 통해 패킷을 전송할 때는 크게 두 가지 형태가 있다. 


여러 패킷을 모아 한번에 전송하거나, 하나의 패킷을 나누어서 여러 번에 걸쳐 전송하는 것이다. 

첫 번째는 데이터 크기가 작을 때 자원을 효율적으로 사용하기 위함이고, 두 번째는 데이터 크기가 클 때 내부 버퍼 용량과 전송 실패시의 부담을 고려한 것이다.



패킷의 정보 구성

데이터 패킷은 Header와 Body로 구성된다. 


헤더에는 한 패킷의 크기를 나타내는 정보가 담겨있다. 데이터 처리시 이 부분을 먼저 읽게 된다. 

바디에는 데이터가 들어있다.



필요에 따라 하나의 패킷이 다른 패킷에 포함될 수도 있다. 


패킷을 수신할 때, 패킷을 처리해 원하는 결과값을 얻을 것이다. 데이터의 종류는 수신 메시지 외에도 여러가지가 있다.

그렇기 때문에 패킷에는 데이터의 종류를 나타내는 정보도 담을 필요가 있다.


패킷 2는 데이터의 종류를 나타내는 헤더와 패킷 1로 구성될 수도 있다. 

(패킷 2 = 데이터의 종류를 나타내는 헤더 + 헤더와 데이터로 구성된 패킷 1)


패킷2의 헤더를 읽고 난 뒤에는 데이터의 종류를 알기에 패킷1의 데이터를 어디에서 처리할지 알 수 있다.

그 다음은 패킷1의 헤더를 통해 실제 데이터 크기를 알고 남은 데이터를 읽게 된다.



패킷1의 헤더에는 데이터의 총 크기가 담겨 있다. 

만약 수신한 데이터가 총 크기보다 작을 경우, 남은 데이터를 모두 읽을 때까지 반복하여 Receive() 함수를 호출하면 될 것이다.



패킷 개념을 적용한 소스는 다음과 같다. 우선 패킷으로 데이터를 수신하도록 Receive 함수의 일부분이 바뀌었다.

private void Receive()  // 상대 호스트로부터 데이터 수신 {     ( ... 생략)         //패킷 개념 적용을 위해 주석처리         //mClientSocket.Receive(data, SocketFlags.None);          data = ReceiveData();   // 정의한 패킷으로 데이터 수신                                                      message = Encoding.Default.GetString(data); // 수신데이터 string 형태로 데이터 변환         mChatWnd.ReceiveMessage(message);   // chat창에 메시지 전달                                     } }

다음과 같이 Send() 메소드의 일부분이 바뀌고 실질적인 데이터 전송은, 추가된 SendData 메소드를 통하도록 바뀌었다.

public void Send(string message)
{
    (... 생략)
 
    data = Encoding.Default.GetBytes(message);
    //mClientSocket.Send(data, 0, data.Length, SocketFlags.None);
    SendData(data);
}    


패킷 헤더의 크기를 상수로 정의하고 다음과 같이 패킷을 통한 전송/수신 메소드들을 추가하도록 한다.

private const int PACKET_HEADER_SIZE = 4;               // 패킷 해더 크기
 
        private byte[] ReceiveData()
        {
            byte[] headerBuffer = new byte[PACKET_HEADER_SIZE];
            byte[] dataBuffer = null;
 
            int totalDataSize = 0;  // 전체 데이터 크기
            int accumulatedDataSize = 0// 누적 수신한 데이터 크기
            int leftDataSize = 0;       // 미 수신 데이터 크기
            int receivedDataSize = 0;   // 총 수신한 데이터 크기
 
            // 데이터 수신. Receive 메소드 호출로 수신한 데이터 크기 저장
            receivedDataSize = mClientSocket.Receive(headerBuffer, 0, 
                PACKET_HEADER_SIZE, SocketFlags.None);
 
            // BitConverter 클래스의 메소드를 사용하기 위해 using System; 추가
            totalDataSize = BitConverter.ToInt32(headerBuffer, 0);
 
            leftDataSize = totalDataSize;
            dataBuffer = new byte[totalDataSize];
 
            while (leftDataSize > 0)
            {                
                receivedDataSize = mClientSocket.Receive(dataBuffer, accumulatedDataSize,
                    leftDataSize, 0); // 데이터 수신
 
                accumulatedDataSize += receivedDataSize; // 총 누적 수신 데이터
                leftDataSize -= receivedDataSize; // 남은 미 수신 데이터
            }
 
            return dataBuffer;  // 수신된 데이터 반환
        } 
 
        private void SendData(byte[] dataBuffer)
        {
            byte[] headerBuffer = new byte[PACKET_HEADER_SIZE];
 
            int totalDataSize = 0;  // 전체 데이터 크기
            int accumulatedDataSize = 0// 누적 전송한 데이터 크기
            int leftDataSize = 0;       // 미 전송 데이터 크기
            int sentDataSize = 0;   // 전송한 데이터 크기
 
            totalDataSize = dataBuffer.Length;
            leftDataSize = totalDataSize - sentDataSize;
                        
            headerBuffer = BitConverter.GetBytes(totalDataSize); // 전송할 데이터 총 크기
            mClientSocket.Send(headerBuffer);   // 전체 데이터 크기 전송
            
            while (leftDataSize > 0)
            {
                sentDataSize = mClientSocket.Send(dataBuffer, accumulatedDataSize,
                    leftDataSize, SocketFlags.None); // 데이터 전송
 
                accumulatedDataSize += sentDataSize; // 총 누적 전송 데이터
                leftDataSize -= sentDataSize; // 남은 미 전송 데이터
            }
        }

이렇게 패킷 개념을 적용해 프로그램을 작성하면 대용량의 데이터 전송/수신시에도 더 안정적으로 작동할 수 있게 된다.

좀 더 세심하게 예외처리까지 한다면 훨씬 안정적인 프로그램이 될 것이다.



c# winform 텍스트 박스 글씨 *로 보이기

TextBox 속성 중 PasswordChar에 *를 입력하면 된다. 이는 로그인 폼의 패스워드 입력을 위한 것이다.

'Study > C#' 카테고리의 다른 글

C# Thread  (0) 2017.04.24
C# MS-SQL 연동  (0) 2017.04.14
C# 예외처리, delegate  (0) 2017.04.10
C# 인터페이스  (0) 2017.04.07
C# 클래스, 메소드  (0) 2017.04.04
728x90

try~catch~finally

using System;
 
namespace Study
{
    class Program
    {
 
        static void Main(string[] args)
        {
            Console.WriteLine("입력 : ");
            string input = Console.ReadLine();
 
            try
            {
                int index = int.Parse(input);
                Console.WriteLine("입력 숫자 : " + index);
            }
            catch (Exception e)
            {
                Console.WriteLine("예외 발생 (int형이 아님)");
                Console.WriteLine(e.GetType());
                return;
            }
            finally
            {
                Console.WriteLine("프로그램 종료");
            }
        }
    }
}

catch에서 return하여 finally 절은 무조건 실행된다.(finally 가 없다면 "프로그램 종료" 문구가 나타나지 않고 종료될 것이다).

finally 절에서 return 키워드 같이 finally 절의 본문을 벗어나는 키워드는 사용할 수 없다.



throw 키워드


예외를 강제로 발생시킨다.

throw new Exception();



윈폼 예외처리


위와 같이 리스트 박스와 콤보박스를 이용해 윈폼을 디자인한다. 


그 다음 Product 클래스를 만들고 다음과 같이 작성한다.

class Product
{
    public string Name { getset; }
    public int Price { getset; }
}



콤보박스와 리스트 박스에 데이터 바인딩을 한다. 데이터 바인딩된 항목 사용에 체크하고 데이터 개체는 Product클래스를 선택한다.


각각 멤버와 값을 선택한다. 



이제 다음과 같이 데이터를 추가하면 데이터 박스와 리스트 박스에, 바인딩 된 데이터가 출력될 것이다.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
 
        productBindingSource.Add(new Product() { Name = "감자", Price = 500 });
        productBindingSource.Add(new Product() { Name = "사과", Price = 700 });
        productBindingSource.Add(new Product() { Name = "고구마", Price = 400 });
        productBindingSource.Add(new Product() { Name = "배추", Price = 600 });
        productBindingSource.Add(new Product() { Name = "감자", Price = 500 });
    }
}

데이터 그리드뷰도 마찬가지로 데이터 소스를 추가해 활용할 수 있다.




delegator (대리자)


델리게이트는 메소드의 매개변수로 메소드를 전달하기 위한 것이다. 델리게이터는 대리자라고 번역하기도 한다. 

말 그대로 어떤 행위를 대신 한다는 것으로, 행위(메소드)를 저장하고 전달하기 위해 나온 개념이다.


델리게이터 선언

delegate void TestDelegate();


델리게이터 자료형으로 변수 초기화

TestDelegate testDelegate = <메서드 이름, 무명 델리게이터, 람다> ( 이것은 델리게이터 변수를 초기화하는 방법이다)



Comparison 델리게이터 ( MSDN : https://msdn.microsoft.com/ko-kr/library/tfakywbh(v=vs.110).aspx )

public delegate int Comparison<in T>(
	T x,
	T y
)



다음은 Comparison 델리게이터를 이용해 상품의 가격을 정렬하는 예제이다. 

아래 예제에서는 Sort 메서드의 매개변수로 SortWithPrice 메소드를 전달한다.

namespace Delegate
{
    class Product
    {
        public string Name { getset; }
        public int Price { getset; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            List<Product> products = new List<Product>()
            {
                new Product() {Name="감자", Price=500},
                new Product() {Name="사과", Price=700},
                new Product() {Name="상추", Price=300},                
            };
 
            products.Sort(SortWithPrice);
 
            foreach (var item in products)
            {
                Console.WriteLine(item.Name + " : " + item.Price);
            }          
        }
 
        static int SortWithPrice(Product a, Product b)
        {
            return a.Price.CompareTo(b.Price);
        }   
    }
}

Comparison은 델리게이트(대리자)로 형식에 맞는 함수를 따로 정의해 두어야 한다.

1
2
3
4
public static int Compare(float a, float b)
{
    return a.CompareTo(b); 
}
cs




'Study > C#' 카테고리의 다른 글

C# MS-SQL 연동  (0) 2017.04.14
C# 네트워크 기본  (1) 2017.04.12
C# 인터페이스  (0) 2017.04.07
C# 클래스, 메소드  (0) 2017.04.04
c# 복습  (0) 2017.03.31
728x90

인터페이스


1
2
3
4
interface ILogger // 첫글자 대문자로 적는건 약속
{
    
}
cs

인터페이스는 public

객체생성이 이뤄지지않는 껍데기


상속받은 자식클래스에서는 부모클래스에서 명시한 기능을 구현해야함

실제구현은 자식클래스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace Interface
{
    interface ICar
    {
        void drive(string msg);
    }
 
    interface IAirplane
    {
        void fly(string msg);
    }
 
    class Machine : ICar
    {
        public void drive(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class Machine2 : IAirplane
    {
        public void fly(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class MainApp
    {
        static void Main(string[] args)
        {
            Machine m1 = new Machine();
            Machine2 m2 = new Machine2();
        }
    }
}



죽음의 다이아몬드 

: c#에서 클래스 다중상속을 허용하지 않는 이유는 죽음의 다이아몬드 때문이다.

프로그램에서 같은 구문이 두 가지 이상의 의미로 해석될 여지가 있어서는 안 된다





하지만, 인터페이스를 사용하면 다중상속이 가능하다.


인터페이스를 사용하는 이유는 다음과 같다.

  • 자식단에서 구현할 메소드를 미리 구현
  • 메소드의 이름만 물려받고 자식클래스에서 재정의


IComparable 인터페이스

인터페이스는 모두 대문자 I 로 시작한다. IComparable 인터페이스는 비교할 때 사용되는 규약이다.

using System; using System.Collections; using System.Collections.Generic; namespace Study {     class Program     {         class Product         {             public string Name { getset; }             public int Price { getset; }             public override string ToString()             {                 return Name + " : " + Price + "원";             }         }               static void Main(string[] args)         {             List<Product> list = new List<Product>()             {                 new Product() {Name= "고구마", Price=1500},                 new Product() {Name= "사과", Price=2400},             };             list.Sort();             foreach(var item in list)             {                 Console.WriteLine(item);             }         }     } }


위의 프로그램은 Sort() 메서드가 정렬의 기준을 알지 못하기 때문에 오류가 발생한다. 이러한 기준을 정해주기 위해 IComparable 인터페이스를 구현한다.

해당 클래스에서 IComparable 인터페이스를 상속받아 사용한다.


IComparable에 커서를 놓고 Ctrl + .  단축키를 누르면 다음과 같이 인터페이스를 쉽게 구현할 수 있다. 



인터페이스 구현을 누르면 다음과 같이 CompareTo 메서드도 자동 생성된다.

public int CompareTo(object obj)
           {
               throw new NotImplementedException();
           }

CompareTo 메서드를 다음과 같이 변경해보자. obj가 Product이면 가격을 비교해 반환하겠다는 뜻이다. 오름차순으로 정렬된다.

public int CompareTo(object obj)
          {
              return this.Price.CompareTo((obj as Product).Price);
          }


내림차 순으로 정렬하고 싶다면 다음과 같이 변경하면 될 것이다.

public int CompareTo(object obj)
{
    return (obj as Product).Price.CompareTo(this.Price);
}


using 키워드와 IDisposable 인터페이스


아래 예제에서는 IDisposable 인터페이스를 상속받은 클래스 Dummy의 인스턴스를 using 블록의 괄호 내에서 생성한다.

Dispose() 메서드를 따로 호출하지 않아도 using 블록을 벗어날 때 호출된다.

using System;

namespace Study
{
    class Program
    {
        class Dummy : IDisposable
        {
            public void Dispose()
            {
                Console.WriteLine("Dispose() 메서드 호출");
            }
        }


        static void Main(string[] args)
        {
            using (Dummy dummy = new Dummy())
            {
 
            }
        }
    }
}



파일 입출력

using System.IO;

파일을 쓸 때 사용하는 StreamWriter는 IDisposable 인터페이스를 구현한 대표적인 클래스이다.


아래 파일 입출력 경로를 보면 @ 기호가 쓰인 것을 볼 수 있다.

문자열 내부에서 \ 기호는 특수한 의미를 지니기 때문에 원래는 \\를 사용해야 한다.


하지만 @ 기호를 붙여 문자열을 만들면 \\기호를 여러번 쓰지않아도 된다. 다만 내부에서 이스케이프 문자를 사용할 수 없게 된다.


using System;
using System.IO;
 
namespace Study
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"c:\tesst\test.txt";
            string textValue = "시간 - " + DateTime.Now;
 
            try
            {
                File.WriteAllText(path, textValue);
                Console.WriteLine("읽은 문자열 : " + File.ReadAllText(path));        
            }
            catch (Exception)
            {
 
                Console.WriteLine("파일을 읽거나 쓸 수 없습니다");
            }
        }
    }
}


스트림으로 쓰기


큰 용량의 파일을 한꺼번에 읽고 쓰는 것은 시스템에 부담이 가기 때문에 파일을 한 줄씩 읽고 쓸 수 있는 다양한 방법이 있다.

StreamWrite 클래스를 이용하면 스트림으로 쓸 수 있다.


StreamWrite 클래스는 TextWriter의 상속을 받는다.

// 요약:
//     System.IO.TextWriter를 구현하여 특정 인코딩의 스트림에 문자를 씁니다.
[Serializable]
[ComVisible(true)]
public class StreamWriter : TextWriter


TextWriter는 abstract class로 MarshalByRefObject, IDisposable의 상속을 받는다.

이를 통해 IDisposable 인터페이스를 구현한 클래스라는 것을 알 수 있다.

public abstract class TextWriter : MarshalByRefObjectIDisposable


다음은 StreamWrite 클래스로 파일에 여러 문자열을 쓰는 예제이다.

static void Main(string[] args)
        {
            string path = @"c:\test\test.txt";
 
            using (StreamWriter writer = new StreamWriter(path))
            {
                writer.WriteLine("안녕");
                writer.WriteLine("여러줄을");
                writer.WriteLine("입력해봄");
            }
 
            Console.WriteLine(File.ReadAllText(path));
        }



StreamReader 클래스로 한줄씩 읽기


아래는 잘못된 예제이다. 한 줄을 읽고 따로 값이 저장되지 않았기에 2번째 줄만 출력된다.

class Program
    {
        static void Main(string[] args)
        {
            string path = @"c:\test\test.txt";
 
            using (StreamReader reader = new StreamReader(path))
            {
                while (reader.ReadLine() != null)
                {                    
                    Console.WriteLine(reader.ReadLine());
                }
            }
        }
    }

바른 예

static void Main(string[] args)
        {
            string path = @"c:\test\test.txt";
 
            using (StreamReader sr = new StreamReader(path))
            {
                while (sr.Peek() >= 0)
                {
                    Console.WriteLine(sr.ReadLine());
                }
            }
        }


using을 사용하지 않은 예

class Program
{
    static void Main(string[] args)
    {
        string path = @"c:\test\test.txt";
 
        StreamReader reader = new StreamReader(path);
        string line;
 
        while ((line = reader.ReadLine()) != null)
                Console.WriteLine(line); 
    }
}


인터페이스, 내용 정리


인터페이스는 다중 상속이 가능하다.

public class MyConnection : Component, IDbConnection, IDisposable
{
}


인스턴스 변수를 가질 수 없다.

오류 : 인터페이스 멤버에는 정의를 사용할 수 없습니다.

interface IBasic
        {
            void TestMethod()
            {
 
            }
        }

인터페이스는 인스턴스 메서드를 가질 수 있다.


인터페이스는 클래스 변수를 가질 수 없다.

인터페이스는 클래스 메서드를 가질 수 없다.


인터페이스는 속성(Property)을 가질 수 있다.

'Study > C#' 카테고리의 다른 글

C# 네트워크 기본  (1) 2017.04.12
C# 예외처리, delegate  (0) 2017.04.10
C# 클래스, 메소드  (0) 2017.04.04
c# 복습  (0) 2017.03.31
headfirst c#-wpf  (0) 2016.11.19
728x90

윈폼에서 코드로 디자인

namespace CS_Windows_Forms
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            myButton.Text = "코드에서 변경!";
            myButton.Width = 200;
 
            for (int i = 0; i < 5; i++)
            {
                Button btn = new Button();
                Controls.Add(btn);
                btn.Location = new Point(2080 + i * 30);
                btn.Text = i + " 동적 생성";
            }
        }
    }
}

결과 화면



폼 디자인 코드에서 생성자 정의하기

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            Button btn = new Button();
            Controls.Add(btn);
            btn.Location = new Point(2080);
            Box box = new Box(btn);
            
        }
    }
    class Box
    {
        public Box(Button btn)
        {
            btn.Text = "버튼 전달";
        }
    }






생성자 정의하고 버튼으로 값 변경하기

namespace CS_Windows_Forms
{
    public partial class Form1 : Form
    {
        Box box;
 
        public Form1()
        {
            InitializeComponent();
 
            Button btn = new Button();
            Controls.Add(btn);
            box = new Box(btn);
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            box.Area();
        }
    }
    class Box
    {
        Button btn;
 
        public Box(Button btn)
        {
            this.btn = btn;
            btn.Text = "버튼 전달";
        }
 
        public void Area()
        {
            if (btn.Width > 250)
                MessageBox.Show("더 이상 늘릴 수 없습니다");
 
            else
            {
                btn.Width += 10;
                btn.Height += 10;
            }
        }
    }
}




static 키워드

static이라는 키워드를 붙인 변수 또는 메서드는 프로그램을 실행하는 순간에 메모리가 올라가게 된다.

이때는 객체의 생성없이 메서드


Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Collections;
  7. using System.Threading;
  8.  
  9. namespace SScore
  10. {
  11.     class Test
  12.     {
  13.         public static int Abs(int n)
  14.         {
  15.             if (n < 0)
  16.                 return -n;
  17.  
  18.             else
  19.                 return n;
  20.         }
  21.     }
  22.  
  23.     class Program
  24.     {
  25.         static void Main(string[] args)
  26.         {
  27.             //Test test = new Test();
  28.             int num = Test.Abs(-10);
  29.             Console.WriteLine(num);
  30.         }
  31.     }
  32. }




C#의 구조체


c#에서도 구조체를 쓸 수 있다. c#에서는 구조체 멤버 변수도 기본적으로 private로 보호된다.


namespace SScore
{
    public struct MyPoint
    {
        public int x;
        int y;
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            MyPoint point;
            Console.WriteLine(point.x);
            Console.WriteLine(point.y); // 오류 : 보호 수준 때문에 'SScore.MyPoint.y'에 액세스할 수 없습니다.	
 
        }
    }
}


private 생성자

기본적으로 생성자는 public 접근 제한자를 쓰지만, 

생성자로 클래스의 인스턴스를 만들 수 없게 할 때는 private 생성자를 사용한다.

namespace SScore
{
    class Hidden
    {
        private Hidden();
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Hidden hidden = new Hidden();   // 오류
        }
    }
}

위와 같이 메인 메소드에서 인스턴스를 생성하려고 하면 다음과 같은 오류를 출력한다.

오류	: 'SScore.Hidden.Hidden()'은(는) abstract, extern 또는 partial로 
            표시되어 있지 않으므로 본문을 선언해야 합니다.

private 생성자를 이용하는 이유는 다음과 같다.


  • 정적 멤버(static member)만 가지고 있을때
  • 팩토리 메서드 패턴에서 팩토리 메서드로만 인스턴트를 생성하게 하고 싶을 때 (디자인 패턴중의 하나로, 부모 클래스에서 숨겨진 자식 클래스의 인스턴스를 만드는 형태)



정적 생성자 (static constructor)

정적 생성자는 정적(static)요소를 초기화 할때에 사용된다.

정적 생성자를 만들 때는 접근 제한자와 매개변수를 사용하지 못한다.

class Sample
{
    public static int value;
 
    static Sample()
    {
        value = 10;
        Console.WriteLine("정적 생성자 호출");            
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        Sample sample = new Sample();   // 이 때 한번만 호출
        Console.WriteLine();
        Console.WriteLine(Sample.value);
    }
}

출력 : 

정적 생성자 호출


10



readonly 키워드 (read only)

읽기 전용 키워드인 readonly로 변수를 선언하면 값을 변경할 수 없게 된다. 

class Sample
   {        
       public readonly int id=0;
 
       public Sample()
       {
           this.id++;
           Console.WriteLine("생성자 호출");            
       }
   }
 
   class Program
   {
       static void Main(string[] args)
       {
           Sample sample = new Sample();   
           Console.WriteLine(sample.id);
       }
   }

읽기 전용 필드의 값을 변경하려 할 때 다음과 같은 오류 메시지를 출력한다. 다만, 생성자에서는 readonly 키워드 변수도 값을 변경할 수 있다.



Getter와 Setter

값을 은닉화하면서 의도치 않은 값으로 초기화하는 걸 방지할 때에는 게터와 세터를 사용한다.

get 접근자는 값을 반환하고, set 접근자는 값을 할당한다.

class Unsigned
{
    private int num;
    public int Num
    {
        get { return num; }
        set
        {
            if (value > 0) { num = value; }
            else { Console.WriteLine("num변수는 양수여야 합니다."); }
        }
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        Unsigned unsigned = new Unsigned();
        unsigned.Num = -10;            
    }
}

Output : 

num변수는 양수여야 합니다.




게터와 세터를 더 쉽게 쓰려면 prop라고 입력하고  tap키를 두번 누르면 코드 조각이 자동으로 생성된다.

public int MyProperty { getset; }


propfull을 입력한 상태에서 tap키를 두 번 누르면 다음 형태의 코드 조각이 자동으로 생성된다.

private int myVar;
 
        public int MyProperty
        {
            get { return myVar; }
            set { myVar = value; }
        }




상속

using System;
using System.Collections;
using System.Collections.Generic;
 
namespace Study
{
    class Animal
    {
        public string name;
        public void Eat()
        {
            Console.WriteLine("냠냠");
        }
        public void Sleep()
        {
            Console.WriteLine("쿨쿨");
        }
    }
 
    class Dog : Animal
    {
        public void Bark()
        {
            Console.WriteLine("왈왈");
        }
    }
    class Cat : Animal
    {
        public void Meow()
        {
            Console.WriteLine("냐옹");
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
 
            List<Animal> Animals = new List<Animal>()
            {
                new Dog(){name="멍멍이"}, new Cat(){name="냐옹이"}
            };
 
            foreach(var item in Animals)
            {
                Console.WriteLine(item.name);
                item.Eat();
                item.Sleep();
 
                ((Dog)item).Bark();
            }
        }
    }
}

위의 프로그램을 실행시키면 다음과 같은 예외가 발생한다.


출력결과 :

멍멍이

냠냠

쿨쿨

왈왈

냐옹이

냠냠

쿨쿨


처리되지 않은 예외: System.InvalidCastException: 'Study.Cat' 형식 개체를 'Study.

Dog' 형식으로 캐스팅할 수 없습니다.


((Dog)item).Bark();

List에 Dog와 Cat 클래스의 인스턴스가 같이 존재하기 때문에 Cat 형식 개체를 Dog 형식 개체로 형변환 할 때 오류가 발생하는 것이다.

이를 해결하기 위해서는 조건문을 통해 item이 Dog객체인지 Cat 객체인지를 판별하게 해야 한다. 


is 키워드를 사용하면 특정 클래스의 객체일 때 true를 반환하게 된다.

다음과 같이 foreach 구문의 내용을 변경하고 실행해보자.


foreach(var item in Animals)
            {
                Console.WriteLine(item.name);
                item.Eat();
                item.Sleep();
 
                if (item is Dog)
                    ((Dog)item).Bark();
 
                else
                    ((Cat)item).Meow();
            }

실행 결과 :

멍멍이

냠냠

쿨쿨

왈왈

냐옹이

냠냠

쿨쿨

냐옹



섀도잉 (Shadowing)


변수의 이름이 겹칠 때 자신과 가장 가까운 변수를 사용한다. 아래 프로그램에서는 메인 메소드 내부가 된다.

class Program
   {
       public static int num = 20;
 
       static void Main(string[] args)
       {
           int num = 40;
           Console.WriteLine(num);
       }
   }

출력 결과 :

40



하이딩(Hiding)과 오버라이딩(Overriding)


부모 클래스와 자식 클래스 사이에는 이를 Hiding 이라고 한다. 상속하는 멤버가 아닌 자신의 클래스 내부의 변수를 사용한다.

숨겨진 변수나 메소드를 쓸 때는, 부모로 형변환을 통해 쓸 수 있다.


Code Snippet
  1. using System;
  2. namespace Study
  3. {
  4.     class Parent
  5.     {
  6.         public int q = 10;
  7.     }
  8.  
  9.     class Child : Parent
  10.     {
  11.         public new string q = "20";
  12.     }
  13.  
  14.     class Program
  15.     {
  16.         static void Main(string[] args)
  17.         {
  18.             Child child = new Child();
  19.             Console.WriteLine(child.q);
  20.             Console.WriteLine("" + ((Parent)child).q);
  21.         }
  22.     }
  23. }


출력결과 

20

10


static void Main(string[] args)
        {
            Child child = new Child();
            Parent parent = child;

            Console.WriteLine(child.q);
            Console.WriteLine(parent.q);
        }


메소드 하이딩

숨기려면 new 키워드를 사용해 하이딩 함을 명확하게 알려주는 것이 좋다. (에러는 띄우지 않지만 경고를 띄움)

메서드는 변수와 달리, 이름이 겹칠 때 하이딩할지 오버라이딩 할지 결정할 수 있기 때문이다.

using System;
namespace Study
{
    class Parent
    {
        public int variable = 273;
 
        public void Method()
        {
            Console.WriteLine("부모 메서드");
        }
    }
 
    class Child : Parent
    {
        public new void Method()
        {
            Console.WriteLine("자식 메서드");
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Child child = new Child();
            child.Method();
            ((Parent)child).Method();
        }
    }
}


메소드 오버라이딩

부모의 메서드를 virtual 키워드로 정의하면, 자식 클래스에서 override 될 수 있다.

이 때 자식의 메소드가 부모 메서드를 덮어 씌우고 새로 정의된다.

using System;
namespace Study
{
    class Parent
    {
        public virtual void Method()
        {
            Console.WriteLine("부모 메서드");
        }
    }
 
    class Child : Parent
    {
        public override void Method()
        {
            Console.WriteLine("자식 메서드");
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Child child = new Child();
            child.Method();
            ((Parent)child).Method();
        }
    }
}

Output :

자식 메서드

자식 메서드




sealed 키워드


클래스에 적용하면 : 절대 상속하지 못하게 함

메서드에 적용하면 : 더 이상 오버라이드하지 못하게 함



abstract(추상) 키워드


클래스에 적용하면 : 무조건 상속해서 쓰게 함. 해당 클래스 자체에서 인스턴스를 만들수 없게 됨


메서드에 적용하면 

: 반드시 오버라이드해서 쓰게 함, 이 때 메서드는 본문을 선언할 수 없으므로, 중괄호 {}를 쓰지 않고 곧바로 세미콜론 ; 으로 문장을 마침.

  abstract 메서드 선언을 위해 클래스도 abstract 클래스가 되어야 함. 따로 virtual 키워드를 쓰지 않아도 됨




Generic

using System;
namespace Study
{
    class Wanted <T>
    {
        public T value;
        public Wanted(T value)
        {
            this.value = value;
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Wanted<string> wantedString = new Wanted<string>("String");
            Wanted<int> wantedint = new Wanted<int>(12);
 
            Console.WriteLine(wantedString.value);
            Console.WriteLine(wantedint.value);
        }
    }
}

T는 형식매개변수(Type Parametor)를 뜻하며, 하나의 약속이다. 이와 같이 변수 value 자료형을 원하는 자료형으로 지정 가능하다.




Out 키워드

using System;

namespace Study
{
    class Program
    {
        static void Main(string[] args)
        {
            string s="0404";
            int output;

            bool result = int.TryParse(s, out output);

            if (result)
            {
                Console.WriteLine(output);
            }
            else
                Console.WriteLine("숫자로 변환불가");
        }
    }
}


'Study > C#' 카테고리의 다른 글

C# 예외처리, delegate  (0) 2017.04.10
C# 인터페이스  (0) 2017.04.07
c# 복습  (0) 2017.03.31
headfirst c#-wpf  (0) 2016.11.19
5/27 업무일지 c#학생성적관리(콘솔)  (0) 2016.05.27
728x90

Object 형식

어떤 형식의 데이터라도 object에 담아 처리할 수 있다. (int, long, char, bool, string...)


ex) object a=123;    object b=3.14f;    object c = true;    object d = "hello";




나머지 연산자 %

실수에 나머지 연산자 % 를 쓰는 건 추천하지 않는다.

static void Main(string[] args)
        {
            Console.WriteLine(5.0 % 2.2);
        }

정수라면 실행 결과로 2를 출력하겠지만 여기서는 0.6을 출력한다.

5.0 - (2.2 * 2) = 0.6 



문자열

문자열은 다음과 같이 더할 수 있다.

하지만 문자 (한 글자)는 더할 수 없고, 문자를 숫자로 생각해 더하게 된다.


문자를 연결하고 싶다면 ' '로 감싸지말고 문자열을 뜻하는 " " 로 감싸야한다. 

static void Main(string[] args)
      {
          Console.WriteLine("가나다" + "라마" + '바');
          Console.WriteLine("안녕하세요" [0]);
          Console.WriteLine("안녕하세요" [2]);
      }

출력 결과 :

가나다라마바



숫자와 문자열을 더하면 자동으로 문자열로 형변환된다.

하지만 숫자와 '문자'를 더하면 아스키 코드값으로 출력된다.

static void Main(string[] args)
       {
           Console.WriteLine(2 + '3' + "4");
           Console.WriteLine(2 + '3' + '4');
       }

출력결과 :

534

105




다른 자료형을 bool로 변환

다음과 같은 경우 23번째 줄에서 에러를 출력한다.
Code Snippet
  1. static void Main(string[] args)
  2.         {
  3.             Console.WriteLine(bool.Parse("True"));
  4.             Console.WriteLine(bool.Parse("true"));
  5.             Console.WriteLine(bool.Parse("test"));
  6.         }


bool은 true, false 데이터를 다루며 1바이트 크기의 type이다. (1비트만으로 표현가능하지만 컴퓨터가 기본적으로 바이트 단위를 사용하므로)



오버플로우 확인하기

static void Main(string[] args)
        {
            int num = int.MaxValue;
            Console.WriteLine(num+1);
        }   

오버플로우를 일으키며 -2147483648 이라는 값을 출력한다.

다음과 같이 double로 형변환하면 해결 가능하다.

static void Main(string[] args)
       {
           int num = int.MaxValue;
           Console.WriteLine((double)num+1);
       }   

혹은 num을 선언할 때 unsigned 자료형으로 선언해 해결할 수도 있다.

uint num = int.MaxValue;




음수의 음수?


Code Snippet
  1. static void Main(string[] args)
  2.         {
  3.             Console.WriteLine(-(-1));
  4.             
  5.             int output = int.MinValue;
  6.             Console.WriteLine(-(output));
  7.             Console.WriteLine(-(-2147483648));
  8.         }


위 소스 25번째 줄에서 다음과 같은 오류를 일으킨다.


checked 모드에서 컴파일하면 작업이 오버플로됩니다.

25번째 줄을 주석처리하면 다음과 같은 결과가 출력된다.

출력결과 :
1
-2147483648


키 입력을 출력
static void Main(string[] args)
        {
            Console.ReadKey();
        }
위의 예제를 실행하면 사용자가 누른 키를 콘솔 화면에 표시한다.


ReadKey() 메서드는 ConsoleKeyInfo 객체를 받게 되고, 이 객체 내부의 KeyChar 속성을 통해 입력 키를 알아낸다.
public static ConsoleKeyInfo ReadKey();
public static ConsoleKeyInfo ReadKey(bool intercept);





지정된 크기의 배열 생성


값을 넣지않고 초기화하면 기본 값으로 공간이 채워진다. (숫자 자료형은 0, 문자열 자료형은 빈 문자열, 객체는 null로 초기화)

static void Main(string[] args)
       {
           int[] intArray = new int[100];
 
           Console.WriteLine(intArray[0+ "\n" + intArray[99]);
       }


배열 요소를 마지막 요소에서부터 출력

static void Main(string[] args)
        {
            int[] intArray = { 123456 };
 
            for (int i = intArray.Length - 1; i >= 0; i--)
                Console.Write(intArray[i] + " ");
        }

출력결과 : 6 5 4 3 2 1



List 클래스를 이용한 가변적인 크기의 배열 생성

List<> 에서 <>안에는 Generic으로 어떤 자료형인지를 미리 알려준다.

static void Main(string[] args)
       {
           Random random = new Random();
 
 
           List<int> list = new List<int>();
 
           for (int i = 0; i < 6; i++ )
               list.Add(random.Next(1100));
 
           for (int i = 0; i < list.Count; i++)
               Console.Write(list.ElementAt(i) + " ");
       }   


List활용예제

이름, 학점, 영어 입력받고 학점 4.0 이상이고 영어점수가 900점 이상인 리스트를 출력


C#에서 클래스는 앞글자 대문자, 변수명은 소문자를 쓴다. (절대 규칙은 아니지만 권장)


이외의 네이밍 규칙 http://zepeh.tistory.com/115


Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Collections;
  7.  
  8.  
  9. namespace SScore
  10. {
  11.     class Student
  12.     {
  13.         public string name;
  14.         public double grade;
  15.         public int eng;
  16.     }
  17.     class Program
  18.     {
  19.         static void Main(string[] args)
  20.         {
  21.             List<Student> list = new List<Student>();
  22.  
  23.             for (int i = 0; i < 3; i++)
  24.             {
  25.                 Student s = new Student();
  26.  
  27.                 Console.Write("이름:");
  28.                 s.name = Console.ReadLine();
  29.                 Console.Write("학점:");
  30.                 s.grade = double.Parse(Console.ReadLine());
  31.                 Console.Write("영어:");
  32.                 s.eng = int.Parse(Console.ReadLine());
  33.  
  34.                 list.Add(s);
  35.             }
  36.             for (int i = 0; i < list.Count; i++)
  37.             {
  38.                 if (list.ElementAt(i).grade < 4.0 || list.ElementAt(i).eng < 900)
  39.                     break;
  40.                    
  41.  
  42.                 Console.WriteLine(list.ElementAt(i).name + "," +
  43.                     list.ElementAt(i).grade + "," +
  44.                     list.ElementAt(i).eng);                
  45.             }
  46.         }
  47.     }
  48. }



Generic

제네릭은 클래스 자료형을 지정하는 기능이다.  형식 매개 변수 T를 사용 하고 선언할 때 Name<T>의 형태를 가진다. 
두 개 이상을 지정하려면 T, U와 같이 쓴다.

namespace Generic
{
    class Wanted<T, U>
    {
        public T value;
        public U value2;
 
        public Wanted(T value, U value2) 
        {
            this.value=value;
            this.value2 = value2;
        }
    }    
    class Program
    {
        static void Main(string[] args)
        {
            Wanted<stringint> StringAndInt = new Wanted<stringint>("String"32);
 
            Console.WriteLine(StringAndInt.value + "\n" + StringAndInt.value2);
        }
    }
제네릭의 가장 일반적인 사용은 컬레션 클래스를 만드는 것이지만, 클래스나 인터페이스, 메서드, 대리자에도 사용된다.


Indexer
인덱서를 이용하면 클래스나 구조체의 인스턴스를 배열처럼 인덱싱할 수 있다.
인덱서는 프로퍼티와 유사하지만 반드시 인스턴스 멤버로만 존재하여야 한다. (static 선언 불가)
class Sign
{
    public int this[int i]
    {
        get { return (-i); }
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        Sign sign = new Sign();
        Console.WriteLine(sign[15]);
    }
}

'Study > C#' 카테고리의 다른 글

C# 인터페이스  (0) 2017.04.07
C# 클래스, 메소드  (0) 2017.04.04
headfirst c#-wpf  (0) 2016.11.19
5/27 업무일지 c#학생성적관리(콘솔)  (0) 2016.05.27
c# 클래스 복습2  (0) 2016.05.23
728x90


타입 강제와 약한 형식화


자바스크립트는 내부적으로 데이터 타입을 변환하여 작업을 완료할 수 있다.

이를 type coercion (타입 강제) 이라고 한다.


'1' > 0 이라는 구문에서 문자열 1이 숫자 1로 강제 변환되고 true를 return할 수 있다.


강력한 형식화를 사용하는 일부 다른 프로그래밍 언어와 달리, 자바스크립트는 약한 형식화를 사용한다.


따라서 의도하지 않은 값을 만들어내는 것이 싫다면 동등 비교 연산자 ==, != 를 쓰는 것보다 

일치 비교 연산자 ===, !== 를 쓰는 것이 권장된다.



===, !== 는 같은 값인지 여부 뿐만 아니라 데이터 타입도 비교한다.




단축 평가 값


OR 연산자를 사용할 때는 true를 리턴할 가능성이 높은 코드를 첫번째 조건으로 작성하고,

AND 연산자를 사용할 때는 false를 리턴할 가능성이 높은 코드를 첫번째 조건으로 작성한다.


단축 평가 기버은 페이지 내에 요소가 존재하는지를 검사하는 용도로 활용이 가능하다.



자바스크립트에서 true와 false

자바스크립트에서 숫자 0이나 빈 값, 값을 대입하지 않은 변수는 false로 취급된다.


반면에 내용을 가진 문자열이나 0이 아닌 다른 숫자는 true이다.



단항 연산자에서 

if( document.getElementById('header') ) 라는 구문이 있다면


ifdocument.getElementById('header'== true ) 로 생각하기 쉽다.

하지만 boolean값인 true와 document.getElementById('header') 가 
리턴되는 객체(true로 취급될 순 있어도)는 엄연히 다른 값이다!

논리 연산자는 항상 true나 false를 리턴하지 않는다. 

처리를 중지하게 된 계기가 된 값을 리턴하거나, 
그 값이 실제로 boolean type이 아니더라도 true나 false로 취급될 수 있다.


예약된 데이터 타입

단순혹은 기본 데이터 타입인 String, Number, Boolean, Undefined, Null

복합데이터 타입인 Object



true, false는 boolean 값이므로 (false === 0)은 

type이 일치하지 않으므로 false를 return한다.


(undefined == null)이 true지만, (undefined === null)은 false이다. 

boolean과 마찬가지로 undefined도 하나의 형식이기 때문이다.





'Study > JavaScript' 카테고리의 다른 글

[JavaScript] Proxy  (0) 2024.04.03
[JavaScript] 기본  (0) 2021.10.18
html + javascript 기초  (0) 2021.01.07
자바 스크립트 기초 - 함수, 메서드, 객체  (0) 2017.03.27
728x90

함수의 매개변수와 인수의 비교


function getArea(width, height) {

return width * height;

}


위 함수내에서 변수 이름과 동일하게 동작하는 (width, height) 가 매개 변수이다.


wallWidth = 3;

wallHeight = 5;

getArea(wallWidth, wallHeight);


위에서 함수가 호출될 때 전달되는 값이 바로 인수이다.



함수로부터 여러 값 전달받기


1
2
3
4
5
6
7
8
9
function getSize(width, height, depth) {
  var area = width * height;
  var volume = width * height * depth;
  var sizes = [area, volume];
  return sizes;    
}
 
var areaOne = getSize(323) [0];
var volumeOne = getSize(323) [1];
cs


8행의 areaOne 변수는 3 * 2 의 연산결과인 사각형 면적을 저장하며, size 배열의 첫번째 값[0] 이다.




익명 함수와 함수 표현식


자바 스크립트 해석기가 표현식을 필요로 하는 곳에 함수를 전달하면, 이 함수는 표현식으로 처리된다. 

(var area = 3*5와 같은 선언이 아닌 함수를 대입하였다)


이런 함수를 함수 표현식이라고 하며, 함수 표현식을 사용할 때는 대부분 함수 이름을 생략한다.


1
2
3
4
5
var area = function (width, height) {
return width * height;
};
 
var size = area(34);
cs


위와 같이 함수의 선언과 함께, 변수에 저장할 때 함수 이름을 생략 가능하다. 

이처럼 이름이 없는 함수를 익명 함수(anonymous function)이라고 한다.


5행에서 이 익명 함수를 변수의 이름으로 호출하는 것을 볼 수 있다.



즉시 호출 함수 표현식 

IIFE(Immediately Invoked Function Expressions: “이피( Iffy )”라고 발음)


이름이 부여되지 않은 함수를 의미한다. 자바 스크립트 해석기가 함수 정의를 만났을 때 단 한번 실행된다.


1
2
3
4
5
var area = (function () { 
    var width = 3;
    var height = 2;
    return width * height;
() );
cs

위의 변수 area는 나중에 호출할 목적으로 함수를 저장하는 것이 아닌, 함수가 리턴한 값만 저장하게 된다.


초록색 괄호는 자바스크립트 해석기에게 함수가 즉시 호출되어야 함을 알린다.


빨간색 괄호그룹화 연산자 이며, 해석기가 전체 코드를 표현식처럼 취급하도록 하기 위해 둘러싼 괄호이다.



이러한 익명 함수와 즉시 호출 함수는 어떤 작업 내에서 단 한번만 실행될 필요가 있을 때 사용된다.

예를 들면 다음과 같다.


  • 함수를 호출할 때 인수로 전달하기 위해(호출할 함수에 전달할 값을 계산하기 위해)
  • 객체의 속성에 값을 대입하기 위해
  • 동일한 변수 이름을 사용하는 두 스크립트 간의 이름 충돌을 방지하기 위해
  • 이벤트 핸들러와 리스너 내부에서 이벤트가 발생했을 때 작업을 처리하기 위해

즉시 호출 함수는 주로 일련의 코드를 감싸기 위해 사용한다.
scope라는 개념 하에 익명 함수 내부에 선언된 모든 변수들은 스크립트의 다른 부분에 선언된 같은 이름의 변수로부터 보호된다.



변수의 범위 scope


함수내부에서 var 키워드를 이용해 함수 내부에 선언하면 해당 함수 내부에서만 사용할 수 있다. 

이를 지역 변수, 함수 수준 변수라고 하며, 지역 범위, 함수 수준 범위를 가지고 있다고 표현한다.


함수 바깥 부분에서 선언한 변수는 전역 변수이며, 전역 범위(global scope)를 가진다.


var 키워드를 사용하지 않아도 전역변수로 취급된다.




객체란?

현실 세계에서 인지한 물체에 대한 모델을 만들기 위해 변수와 함수를 그룹화 한 것.


객체의 일부로 선언된 변수는 속성(property)이라고 부른다.

객체의 일부로 선언된 함수는 메서드(method)라고 부른다.


다음은, 축약형 표기법을 이용해 객체 생성 

(app간에 데이터를 저장하거나 전송할 때 또는 페이지를 위한 정보를 설정하는 설정 객체나 전역 객체를 생성할 때 권장됨)


var hotel = {
  name : 'Quay',
  rooms : 40,
  booked : 25,
  checkAvailability : function() {
    return this.rooms - this.booked
  }
};


객체 내에서의 속성 이름을 key라고 부른다. 이 키를 통해 해당 값에 접근하기 때문에 중복은 불가하다.

: 왼쪽이 속성 이름이며, 오른쪽이 값이다. 여러 개의 속성을 선언할 때 쉼표로 구분한다.


객체에 접근할 때는 멤버 접근 연산자 . 을 통해 접근한다.


1
var hotelName = hotel.name;
cs


또는 아래와 같이 대괄호 문법을 통해 접근 가능하다.

1
var hotelName = hotel['name'];
cs



빈 객체를 축약형 표기법으로 생성

1
var hotelName = {};
cs



객체 수정하기


1
hotel.name = 'Park';
cs


대입 연산자를 통해 새로운 속성값을 대입하면 된다. 객체가 가지고 있지 않은 속성에 대입하려하면, 객체에 새 속성이 추가된다.

대괄호 문법으로도 속성값 변경이 가능하다.


1
hotel['name'= 'Park';
cs


속성 제거하기

다음과 같이 delete 키워드를 이용한다.


1
delete hotel.name;
cs


속성 값만 제거하기

다음과 같이 빈 문자열을 대입한다.

1
hotel.name = '';
cs



객체 생성자


function Hotel(name, rooms, booked) {
  this.name = name;
  this.rooms = rooms;
  this.booked = booked;
  
  this.checkAvailability = function() {
    return this.rooms - this.booked;
  }
}

위 함수는 호텔을 표현하는 새 객체를 만들기 위한 템플릿처럼 사용된다.
객체 생성자 함수의 이름은 주로 대문자로 시작한다. (일반적으로 함수 이름은 소문자로 시작한다)

객체 생성자에서 각각의 구문은 새로운 속성이나 메소드를 대입하는 것이므로 semi-colon으로 끝을 맺는다.

new 키워드를 이용한 객체 생성

quayHotel과 parkHotel 객체 생성,  Hotel('Quay'4025)는 생성자 함수이다.

var quayHotel = new Hotel('Quay'4025);
var parkHotel = new Hotel('Park'12077);


아래 예제에서는 생성자 함수를 이용해 hotel 이란 이름의 빈 객체를 생성한다.
객체 생성후엔 속성과 메소드를 대입하는데, 이미 가지고 있는 속성이 있다면 값을 덮어 쓰게 된다.

// Set up the object
var hotel = new Object();

hotel.name = 'Park';
hotel.rooms = 120;
hotel.booked = 77;
hotel.checkAvailability = function() {
  return this.rooms - this.booked;  
};

var elName = document.getElementById('hotelName'); // Get element
elName.textContent = hotel.name;                   // Update HTML with property of the object

var elRooms = document.getElementById('rooms');    // Get element
elRooms.textContent = hotel.checkAvailability();   // Update HTML with result of method




객체 생성자 표기법을 이용해 객체 생성 및 사용하기


// 호텔 객체를 위한 생성자 함수 템플릿을 정의

function Hotel(name, rooms, booked) {
  this.name = name;
  this.rooms = rooms;
  this.booked = booked;
  this.checkAvailability = function() {
    return this.rooms - this.booked;
  };
}


// 두 개의 다른 호텔 객체 인스턴스 생성

var quayHotel = new Hotel('Quay'4025);
var parkHotel = new Hotel('Park'12077);


// 이미 존재하는 변수에 += 연산자를 사용해 다른 데이터를 덧붙인다.
var details1 = quayHotel.name + ' 남은 방: ';
    details1 += quayHotel.checkAvailability();
var elHotel1 = document.getElementById('hotel1');
elHotel1.textContent = details1;

var details2 = parkHotel.name + ' 남은 방: ';
    details2 += parkHotel.checkAvailability();
var elHotel2 = document.getElementById('hotel2');
elHotel2.textContent = details2;



한글 때문에 html의 글자가 깨진다면 html 소스에 다음 코드를 추가한다.

<meta charset="utf-8">


속성을  추가하거나 제거하기
이미 hotel 객체의 인스턴스를 생성한 후에도 속성을 추가하거나 제거할 수 있다.

var hotel = {
  name : 'Park',
  rooms : 120,
  booked : 77
};

// gym, pool 속성을 추가하고 boolean 값을 대입 후 booked 속성을 제거

hotel.gym = true;
hotel.pool = false;
delete hotel.booked;




this 키워드


var shape = {
  width: 600,
  height: 400,
  getArea: function() {
    return this.width * this.height;
  }
}
이 키워드는 항상 단 하나의 객체를 참조하며, 함수를 실행하고 있는 객체를 가리킨다.
바꿔 말해, 함수 내부에서 this 키워드가 사용될 때, this 키워드는 함수를 감싸는 함수가 소속된 객체를 가리킨다는 것이다.

한 함수가 다른 함수 내에 중첩되어 있는 경우에는, 첫번째 함수 내의 변수에 this 키워드의 값을 저장해두고, 

중첩된 함수내에서 이 변수를 this 키워드 대신 사용하는 것이 권장된다.



function windowSize() {
  var width = this.innerWidth;
  var height = this.innerHeight;
  return [height, width];
}

스크립트의 최상위 수준 (다른 객체나 함수 내부가 아닌 곳)에 함수를 선언하면, 이 함수는 global scope 혹은 global context에 포함된다.

이 global context에 속한 객체는 window 객체이므로 this 키워드를 사용하면 window 객체를 가리키게 된다.



아래 예제에서는 축약형 표기로 객체를 생성했다. 하지만 전역 범위에 선언된 showWidth() 함수는 객체의 멤버가 아니므로,

이 경우 전역 변수 width 를 가리킨다.


var width = 600;


var shape = {
    width: 300
};


var showWidth = function() {
  document.write(this.width);
};


showWidth();



메서드로서의 함수 표현식


아래 예제의 11행은 익명 함수가 shape 객체의 메서드로 사용될 것이며, 그 이름이 getWidth() 함수가 될 것임을 표현한 것이다.

getWidth() 함수를 호출하면, 실제로 showWidth() 함수가 호출된다. 
이 때는 그 함수가 속한 객체가 shape이므로 shape 객체의 속성 width의 값 300을 가리키게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
var width = 600;
 
var shape = {
    width: 300
};
 
var showWidth = function() {
  document.write(this.width);
};
 
shape.getWidth = showWidth;
shape.getWidth();
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
var funcThis = null
 
function Func(){
    funcThis = this;
}
var o1 = Func();
if(funcThis === window){
    document.write('window <br />');
}
 
var o2 = new Func();
if(funcThis === o2){
    document.write('o2 <br />');
}
cs


결과

1
2
window 
o2
cs



6행에서는 함수를 호출한 것이므로 함수에서의 this는 그 함수가 속한 객체, 여기서는 window 객체를 가리킨다.

11행에서는 new 키워드를 통해 생성자를 통해 만들어진 객체에서의 this 는 그 만들어진 객체 자신을 가리킨다.



배열도 객체이다


호텔 방의 가격을 객체로 표현한 경우


costs = {
  room1: 420,
  room2: 460,
  room3: 230,
};


이 경우 room1~3 속성과 각각의 값을 가진다.

호텔 방의 가격을 배열로 표현한 경우


costs = [420460230];

이 경우 index 0~2 와 각각의 값을 가진다.


객체 내에 배열을 가지거나 배열 내에 객체를 가질 수 있다.

객체 내의 배열
costs.room1.items[0];

배열 내의 객체
costs[2].phone;


배열의 2번째 index  { accom: 620, phone: 60 }




내장 객체

브라우저 객체 모델, 문서 객체 모델, 전역 자바스크립트 객체



브라우저 객체 모델

window.screen.width;


문서 객체 모델

documnet.getElementById('one');


전역 자바스크립트 객체

hotel.toUpperCase();    


: string 객체가 전역 객체로 제공되며 문자열 자체가 마치 String 객체인 것처럼 동작하기 때문에 wrapper object라고도 함



예약된 데이터 타입


단순혹은 기본 데이터 타입인 String, Number, Boolean, Undefined, Null

복합데이터 타입인 Object



배열이 객체라는 것은 .length라는 속성을 통해 확인할 수 있다.




'Study > JavaScript' 카테고리의 다른 글

[JavaScript] Proxy  (0) 2024.04.03
[JavaScript] 기본  (0) 2021.10.18
html + javascript 기초  (0) 2021.01.07
자바스크립트 - 의사 결정  (0) 2017.03.28
728x90

열거형

열거형의 멤버는 차례대로 0의 값을 갖는다. 하지만 선언할 때 초기값을 원하는 값으로 설정할 수도 있다.

#include <stdio.h>
 
enum season
{
	spring = 1, summer, fall, winter
}Season;
 
void main(void)
{	
	printf("계절을 고르세요 -> \n 1.봄  2.여름  3.가을  4.겨울 : ");
	scanf("%i" , &Season);
	
	switch (Season)
	{
	case spring:
		printf("봄은 꽃 피는 계절\n");
		break;
	case summer:
		printf("여름은 바캉스의 계절\n");
		break;
	case fall:
		printf("가을은 낙엽이 밟히는 계절\n");
		break;
	case winter:
		printf("겨울은 눈 내리는 계절\n");
		break;
	default:
		break;
	}
}


auto 키워드


원래 C에서의 의미와 C++에서의 의미

https://dojang.io/mod/page/view.php?id=803


c에서도 다음과 같이 선언하고 실행 결과를 얻을 수 있다. (http://codepad.org/kzaEWQRj)

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

auto func() {
	return 10;
}

void main() {
	int a;
	a = func();
	printf("%d \n", a);
}

Output:
1
10 


구조체 변수를 함수 매개변수에 사용하기


구조체 변수는 대입 연산이 가능하므로 함수의 매개변수로 주거나 반환받을 수 있다.

예를 들어, 함수에서 여러 개의 값을 구조체로 묶어 동시에 반환할 수 있다.


두 변수의 값을 바꾸는 함수는 포인터가 필요하지만, 구조체 변수를 사용해 값을 주고 받으면 

포인터 없이도 두 변수의 값을 바꾸는 함수를 만들 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
typedef struct
{
    double left;
    double right;
} vision;
 
vision exchange(vision);    // 구조체를 통해 두 변수의 값을 교환
 
int main(void) {
    vision robot;
    printf("시력 입력 : ");
    scanf_s("%lf %lf"&(robot.left), &(robot.right));
    robot = exchange(robot);
    printf("바뀐 시력 : %.1lf, %.1lf \n", robot.left, robot.right);
 
    return 0;
}
 
vision exchange(vision robot)
{
    double temp;
    temp = robot.left;
    robot.left = robot.right;
    robot.right = temp;
 
    return robot;
};
cs


Output :

시력 입력 : 0.5 1.5

바뀐 시력 : 1.5, 0.5




비트 필드 구조체

1
2
3
4
5
6
struct children
{
    unsigned int son 2;
    unsigned int daughter 2;
    unsigned int pet 3;
};
cs

비트 필드 구조체를 이용하면, 멤버의 크기를 비트 단위로 설정할 수 있다. colon(:) 뒤의 숫자가 멤버가 사용하는 비트 수 이다.

저장할 수 있는 값의 범위는 비트 수에 따라 2의 n승. (2비트는 0~3, 3비트는 0~7)


signed를 이용하면 음수도 저장할 수 있으나, 부호비트 처리방식이 시스템에 따라 다를 수 있으므로 unsigned를 주로 사용



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
typedef struct
{
    unsigned int son : 4;
    unsigned int daughter : 4;
} children;
 
int main(void) {
    children child = { 12 };    // 구조체 변수 초기화
    int sum;
 
    printf("children 구조체 크기 : %d 바이트 \n"sizeof(child) );
    sum = child.son + child.daughter;
    printf("자식 수 : %d \n", sum);
}
cs

Output : 

children 구조체 크기 : 4 바이트

자식 수 : 3


자료형은 정수형만 사용하며, 멤버가 가질 수 있는 최대 비트 수를 결정

unsigned int형은 최대 32비트까지만 지정 가능. 

위에서 children 구조체 멤버의 비트 수를 모두 합해도 7비트지만, unsigned int형 크기인 32비트가 메모리에 할당된다.

(멤버가 할당되지 않은 영역은 사용하지 않는 영역이 됨)


만약 기본 할당된 영역을 넘어서면 새로운 바이트 공간을 사용하면서 멤버가 할당되게 된다.


패딩 비트 (사용하지 않는 공간)

만약, 멤버의 이름을 생략하면 사용하지 않는 패딩 비트가 된다.



                 

 

 

 b

 b

 b

 패딩

  비트

 a


 

 

 

 

 c 

 c

 c 

 c 



배열을 멤버로 선언하거나 멤버의 주소를 직접 구할 순 없다.

키보드로 입력을 받아들이려면 별도의 변수에 입력한 후에 멤버에 대입해야 한다.

6행의 주석을 제거하고 실행한다면, 에러가 난다. 비트 필드 구조체 멤버의 주소를 직접 구할 수 없기 때문이다.


1
2
3
4
5
6
7
8
int main(void) {
    children child;
    int temp;
    scanf("%d"&temp);
    child.son = temp;
    //* scanf("%d", child.son); /* 에러 */
    printf("%d \n", child.son);
}
cs



구조체 포인터와 -> 연산자


child. 과 같이 구조체 멤버에 직접 접근하는 거 외에도, 구조체를 가리키는 포인터를 통해 멤버에 접근할 수도 있다.


구조체 변수 전체의 주소를 구해, 그 값을 저장하는 것이 바로 구조체 포인터이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
typedef struct
{
    int son;
    int daughter;
} children;
 
int main(void) {
    children child = { 12 };
    children *ps = &child;
        
    printf("아들은 몇 명? : %d \n", (*ps).son);
    printf("딸은 몇 명? : %d \n", ps->daughter);
}
cs


children 구조체를 가리키는 포인터 변수 ps에, child의 주소 연산 (&)을 통해 구한 변수의 주소값을 저장하는 것이다.

그러면 ps는 구조체 변수 child를 가리키게 된다.


멤버 접근 연산자인 . 연산자가 * 연산자 보다 우선 순위가 높기 때문에, * 연산자가 먼저 수행될 수 있도록 다음과 같이 괄호로 묶는 것이다.

(*ps).son


이것을 사용하기 편하게 해주는 것이 -> 연산자이다.

(*ps).son    ==      ps->son


 

'Study > C' 카테고리의 다른 글

size_t  (0) 2017.05.29
C 복습-콘솔 입출력  (0) 2017.02.11
c 복습  (0) 2016.11.03
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
728x90

참고 강의 : https://wikidocs.net/book/31

 

 

인터페이스

 

인터페이스의 필요성

 

육식 동물에게 먹이를 주는 사육사(ZooKeeper) 클래스가 있다고 하자. 그리고 Animal 클래스를 상속하는 Tiger와 Lion 클래스가 있다.

 

ZooKeeper 클래스의 feed 메소드는 메소드 오버로딩 되어 있다. (입력값이 각각 Tiger, Lion으로 자료형이 다름)

1
2
3
4
5
6
7
8
9
public class ZooKeeper {    
    public void feed(Tiger tiger) {
        System.out.println("feed tiger");
    }
 
    public void feed(Lion lion) {
        System.out.println("feed lion");
    }
}
cs

 

하지만 이 경우, Animal을 상속하는 새로운 동물 클래스가 생길 때 마다, ZooKeeper에도 feed 메소드가 필요하게 된다.

 

이럴때 필요한 것이 바로 "인터페이스"이다.

 

 


 

Predator.java

 

1
2
public interface Predator {
}
cs

육식 동물 인터페이스를 작성했다. class 키워드가 아닌 interface 키워드를 사용해 구현한다.

 

그리고 Tiger와 Lion 클래스는 Animal 클래스를 사용하며, Predator 인터페이스를 구현하도록 한다. 이 때는 implements라는 키워드를 사용한다.

1
2
public class Tiger extends Animal implements Predator {    
}
cs

 

1
2
public class Lion extends Animal implements Predator { 
}
cs

 

이제부터는 어떤 육식 동물이 추가되더라도 다음과 같이 인터페이스를 구현한 클래스를 정의하면 된다.

1
2
public class Crocodile extends Animal implements Predator {
}
cs

 

하지만 아직도 문제는 있다. 

1
2
3
public void feed(Predator predator) {
    System.out.println("feed ???");
}
cs

 

ZooKeeper 클래스가 육식동물(클래스)의 이름(name)에 따라 다른 문자열을 출력해야 할 것이다.

 

이럴 때 인터페이스에 새로운 메소드 추가가 필요하다.

 


 

Predator.java

 

1
2
3
public interface Predator {
    public String getName();
}
cs

위에서 특이한 점은 getName 메소드에 몸통(body)가 없다는 점이다. 보통은 메소드명(입력값) { 몸통 } 으로 정의되어야 할 것인데 말이다.

이것은 인터페이스의 규칙으로 위의 getName이라는 메소드는, 다음과 같이 인터페이스를 implements한 클래스들이 구현해야 한다.

 

1
2
3
4
5
public class Tiger extends Animal implements Predator {
    public String getName() {
        return this.name;
    }
}
cs

 

이제 ZooKeeper 클래스의 feed 메소드를 다음과 같이 정의하면 육식동물의 이름에 따라 다른 문자열을 출력할 것이다.

1
2
3
4
5
public class ZooKeeper {    
    public void feed(Predator predator) {
        System.out.println("feed "+predator.getName());
    }
}
cs

predator.getName()을 호출하면, Predator 인터페이스를 구현한 구현체(Tiger, Lion)의 getName()이 호출된다.

 

 

Tiger와 Lion에 있는 getName 메소드가 중복되어 있으므로, 제거하고 Animal 클래스에 getName 메소드를 구현하도록 한다. 

(육식동물 클래스는 Animal클래스를 상속받으니깐)

이렇게 중복된 메소드를 제거하고, 이동하는 행위를 리팩토링(Refactoring)이라고 한다.

 

메인 메소드도 다음과 같이 정의하고 실행결과를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ZooKeeper {    
    public void feed(Predator predator) {
        System.out.println("feed "+predator.getName());
    }
 
    public static void main(String[] args) {
        Tiger tiger = new Tiger();
        tiger.setName("tiger");
 
        Lion lion = new Lion();
        lion.setName("lion");
 
        ZooKeeper zooKeeper = new ZooKeeper();
        zooKeeper.feed(tiger);
        zooKeeper.feed(lion);
    }
}
cs

Output : 

feed tiger

feed lion

 

 

인터페이스의 개념적인 이해

 

컴퓨터의 USB 포트에는 다양한 기기를 연결할 수 있다. 바로 이 USB포트가 물리적 세계의 인터페이스라고 할 수 있다.

USB포트의 규격만 알면 어떤 기기도 만들 수 있고, 컴퓨터는 USB 포트만 제공하고 어떤 기기가 만들어지는 지 신경쓸 필요가 없다. 바로 이 점이 인터페이스의 핵심이다.

 

위에서 만든 사육사(ZooKeeper) 클래스가 호랑이던 사자던 상관하지 않고 먹이를 줄 수 있는것과 같다.

물리적세계 자바세계
컴퓨터 ZooKeeper
USB 포트 Predator
하드디스크, 디지털카메라,... Tiger, Lion, Crocodile,...

※ USB 포트에는 전자기기들이 지켜야만 하는 각종 규칙들이 있다. (인터페이스의 메소드)

'Study > Java' 카테고리의 다른 글

Java Network 프로그래밍 기초  (0) 2017.06.03
NIO 기반 네트워킹  (0) 2017.05.30
JAVA - NIO  (0) 2017.05.29
Java - OOP_클래스와 상속, 생성자, 오버로딩/오버라이딩  (0) 2017.03.17
Java 개발PC 설정 및 기초  (0) 2017.03.16

+ Recent posts