05
29

1) 종료자(finalizer)

종료자 혹은 파괴자라고 하며 해당 객체가 제거되는 시점에 실행된다.

class 클래스명
{
	~클래스명()
    {
    	//...[자원 해제를 위한 코드]
    }
}

종료자는 이름이 ~를 접두사로 쓰는 클래스명과 동일하며 어떤 인자나 반환 값도 갖지 않는다. 종료자가 실행되는 시점은 예측할 수 없다. 

 

ex)

class Car
{
	//##########상태 정보###########
	//속도
	public int i_speed; // 클래스 변수

	//##########행위 정보###########

	// 일반 메서드일 경우
	// 반환타입 메서드이름(매개변수)
	// {
	// }

	// 생성자 메서드의 경우
	// 클래스이름(매개변수)
	// {
	// }

	public Car() // 디폴트 생성자
	{
		i_speed = 0;
		Console.WriteLine("Car() 클래스 생성됨");
	}

	public Car(int i_num)
	{
		i_speed = i_num;
		Console.WriteLine("Car(int) 클래스 생성됨");
	}

	~Car()
	{
		Console.WriteLine("Car() 종료자 호출됨 {0}", i_speed);
	}


	//가속한다
	public void Acc() // 클래스 메서드
	{
		i_speed = i_speed + 10;
	}
	//감속한다
	public void DeAcc() // 클래스 메서드
	{
		i_speed = i_speed - 10;
	}


	public void PrintStatus()
	{
		Console.WriteLine("현재 속도는 {0}Km입니다.", i_speed);
	}
}

static void Main(string[] args)
{
	Console.WriteLine("1===========================");
	Car MyCar = new Car();
	MyCar.PrintStatus();
	Console.WriteLine("2===========================");
	Car MyCar1 = new Car(100);
	MyCar1.PrintStatus();
	Console.WriteLine("3===========================");
	new Car(1);
}

 

마지막에 생성된 객체부터 종료자가 생성됨을 볼 수 있다.

예제 실행 화면

 

메모리에 데이터를 할당만 하고 제거를 하지 않으면 언젠가는 주소 공간이 바닥난다. CLR에서는 이런 문제를 내부적으로 가비지 수집가(Garbage Collector)라는 개념을 도입해서 해결하고 있다. 한 번에 몰아서 쓰레기 값을 청소하는데 CPU 사용률이 높을 때는 과부하가 걸릴 수 있다. 따라서 종료자를 사용할 때는 신중하게 해야 한다. GC는 종료자가 정의된 클래스의 객체를 관리하려면 더 복잡한 과정이 거쳐지므로 닷넷이 관리하지 않는 시스템 자원을 얻은 경우에만 종료자를 정의하는 것이 좋다.

 

 

2) 정적 멤버(static), 인스턴스 멤버

어떤 타입을 실체화한 것을 객체, 인스턴스라고 한다. 객체와 관련된 멤버를 인스턴스 멤버(instance member)라고 하며 필드, 메서드, 생성자는 모두 여기에 속한다. 하지만 해당 인스턴스의 타입 전체에 걸쳐 전역적으로 적용되는 필드, 메서드, 생성자가 필요할 수 있는데,  이러한 멤버를 인스턴스 멤버와 구분해 정적 멤버(static member)라고 한다.

 

일반 변수와 static 변수의 선언

 

  • Stack, Heap : 실행할 때(Run-time) 메모리가 만들어지고 실행 시에만 존재
  • static : 실행파일 안에(Compile-time) 이미 만들어져 있고 메모리에 복사를 함 (항상 존재)

 

2.1) 정적 필드(static field)

  • 필드 정의에 static 예약어가 붙음
  • [클래스이름].[정적필드] 형태로 접근

정적 필드의 값은 new로 할당된 인스턴스와 상관없이 존재한다. 정적 필드를 사용하는 경우는 보통 특정 클래스의 인스턴스를 의도적으로 단 한 개만 만들고 싶은 경우다. 

 

ex)

class Person
{
	public static string _name;


	public void OutputYourName() // 인스턴스 메서드
	{
		Console.WriteLine(_name);
	}
}

class Program
{
	static void Main(string[] args)
	{
		Person._name = "이순신";
		Console.WriteLine(Person._name);
	}
}

 

예제 실행 화면

 

class Person
{
	public int CountOfInstance;
	public string _name; // 인스턴스 필드

	public Person(string name) // 인스턴스 생성자
	{
		CountOfInstance++; // 생성자에서 필드 값 증가
		_name = name;
	}

	public void OutputYourName() // 인스턴스 메서드
	{
		Console.WriteLine(_name);
	}
}

class Program
{
	static void Main(string[] args)
	{
		Person person1 = new Person("홍길동");
		Console.WriteLine(person1.CountOfInstance);

		Person person2 = new Person("홍길순");
		Console.WriteLine(person2.CountOfInstance);
	}
}

 

예제 실행 화면

 

class Person
{
	public static int CountOfInstance;
	public string _name; // 인스턴스 필드

	public Person(string name) // 인스턴스 생성자
	{
		CountOfInstance++; // 생성자에서 필드 값 증가
		_name = name;
	}

	public void OutputYourName() // 인스턴스 메서드
	{
		Console.WriteLine(_name);
	}
}

class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine(Person.CountOfInstance);
		Person person1 = new Person("홍길동");

		Person person2 = new Person("홍길순");
		Console.WriteLine(Person.CountOfInstance);
	}
}

 

예제 실행 화면

 

class Person
{
    public string _name;

    public Person(string name)
    {
        _name = name;
        Console.WriteLine("일반 생성자");
    }

    static Person()
    {
        Console.WriteLine("스태틱 생성자");
    }
    public Person()
    {
        Console.WriteLine("디폴트 생성자");
    }
}


namespace _2020_06_01_001
{

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("시작1");
            Person person1;
            Console.WriteLine("시작2");
            person1 = new Person();
            Console.WriteLine("시작3");
            Person person2 = new Person("");
            Console.WriteLine("시작4");
        }
    }
}

 

예제 실행 화면

 

2.2) 정적 메서드(static method)

정적 메서드는 일반 메서드에  static 예약어를 붙여서 정의한다. 정적 메서드는 [클래스이름].[정적메서드] 형태로 호출할 수 있다. 

 

ex)

class SFactory
{
	int iNum;
	static int iNumStatic;

	void SetNumThree1()
	{
		iNum = 3;
		iNumStatic = 3;
		SetNumThree2();
	}

	static void SetNumThree2()
	{
		//iNum = 3; // static method는 static 멤버(변수)만 접근 가능
		iNumStatic = 3;
		//SetNumThree1(); // static method는 static 멤버(method)만 접근 가능
	}
}
        
static void Main(string[] args)
{

}

 

2.3) 정적 생성자(static constructor)

  • 주로 정적 멤버를 초기화하는 기능을 한다.
  • 형식 이니셜라이저(type initializer)라고도 한다.
  • 정적 생성자는 단 한 개만 정의할 수 있고 매개변수를 포함할 수 없다.
  • 정적 생성자의 실행이 실패하는 경우 해당 클래스 자체를 전혀 사용할 수 없다.
  • C# 컴파일러는 정적 필드를 초기화하는 코드를 자동으로 정적 생성자로 옮겨 컴파일한다.
  • 클래스의 어떤 멤버든 최초로 접근하는 시점에 단 한 번만 실행된다.
  • 정적 멤버를 처음 호출할 경우이거나 인스턴스 생성자를 통해 객체가 만들어지는 시점이 되면 그 어떤 코드보다 우선적으로 실행된다. 
class 클래스_명
{
	static 클래스_명()
    {
    	//단 한번, 가장 최초로 실행될 초기화 코드
    }
}
// 자바, C++에는 없음

 

ex)

class Person
{
    public string _name;

    public Person(string name)
    {
        _name = name;
        Console.WriteLine("ctor 실행");
    }

    static Person()
    {
        Console.WriteLine("cctor 실행");
    }
}


namespace _2020_05_29_005
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
            Person person1 = new Person("");
            Console.WriteLine("---------------");
            Person person2 = new Person("");
            */
            Console.WriteLine("시작1");
            Person person1;
            Console.WriteLine("시작2");
            person1 = new Person("");
            Console.WriteLine("==========");
            Person person2 = new Person("");
        }
    }
}

 

예제 실행 화면

 

cf. 정적(static) / 동적(Dynamic)

① 정적(static)

  • Compile-time(번역되는 시점)
  • ex) static
  • 문법 에러가 여기에 해당

② 동적(Dynamic)

  • Run-time(실행되는 시점)
  • ex) new
  • 알고리즘 에러가 여기에 해당

 

cf. 싱글톤(singleton)

해당 클래스의 인스턴스가 하나만 생성이 되는 것을 보장하며 어디서든지 그 인스턴스에 접근이 가능하도록 만드는 패턴

 

3) 네임스페이스(namespace)

  • 이름이 중복되어 정의된 것을 구분하려는 의도에서 나온 것이다.
  • 수많은 클래스를 분류하는 방법으로 주로 사용된다.
  • namespace로 구분된 블록 내에서는 동일한 이름 공간이 적용된다.
  • 네임스페이스가 다른 곳에서 클래스를 생성해야 한다면 해당 클래스가 속한 네임스페이스까지 모두 명시해야 한다. [네임스페이스].[클래스]
  • 네임스페이스는 그 안에 또 다른 네임스페이스를 중첩하는 것도 가능하기 때문에 이름 충돌로 인한 문제는 설령 발생하더라도 대부분 손쉽게 해결할 수 있다.

 

ex)

namespace Communication
{
	class Http
    {
    }
    
    class Ftp
    {
    }
}

namespace Disk.FileSystem
{
	class File
    {
    }
}

namespace ConsoleApp1
{
	class Program
    {
    	static void Main(string[] args)
        {
        	Commnication.Http http = new Commnication.Http();
            Disk.FileSystem.File file = new Disk.FileSystem.File();
        }
    }
}

 

매번 객체 생성 시마다 네임스페이스를 함께 지정하려면 소스가 길어진다. 따라서 C#은 using이라는 예약어를 이용해 다음과 같이 네임스페이스를 미리 선언해두면 객체를 생성할 때 이를 생략해도 C# 컴파일러가 알아서 객체가 속한 네임스페이스를 찾아내어 오류 없이 컴파일한다. using 문은 반드시 파일의 첫 부분에 있어야 한다. 어떤 코드도 using 문 앞에 와서는 안 된다.

 

using Communication;
using Disk.FileSystem;

namespace Communication
{
	class Http
    {
    }
    
    class Ftp
    {
    }
}

namespace Disk.FileSystem
{
	class File
    {
    }
}

namespace ConsoleApp1
{
	class Program
    {
    	static void Main(string[] args)
        {
        	Http http = new Http();
            File file = new File();
        }
    }
}

 

cf. Main문에 input data(인자) 넣기

① 소스파일 생성 후 소스코드 작성

static void Main(string[] args)
{
	//Console.WriteLine(args[0]);
	//Console.WriteLine(args[1]);
	Console.WriteLine(args.Length);
}

 

Developer Command Prompt for VS 2019 실행

 

③ 소스파일이 있는 위치로 Path를 이동

 

cd 디렉토리_패스

 

④ 소스파일 컴파일

 

csc 소스코드명.cs

 

⑤ 생성된 실행파일에 데이터 입력 / 입력한 개수가 출력

 

소스코드명(또는 소스코드명.exe) 입력, ....

 

ex)

//명령 실행시 매개변수 개수를 자동으로 인지하여 출력하는 반복문 작성
// 방법1
/*
int iCount;
for(iCount = 0; iCount < args.Length ; iCount++)
{
	Console.WriteLine("[{0}] : {1}", iCount, args[iCount]);
}
*/

// 방법2
foreach(string command in args)
{
	Console.WriteLine(command);
}

 

예제 실행 화면

 

COMMENT