실행 중(런 타임)에 클래스나 객체의 타입 정보를 조사하는 기능, 리플렉션을 사용하여 형식의 인스턴스를 동적으로 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져오고 해당 메서드와 속성을 액세스 할 수 있다.(형식 이름, 포로퍼티, 메소드, 필드, 이벤트 목록을 모두 볼수 있고, 해당 메소드를 호출하거나 필드, 프로퍼티에 접근하는 것도 가능하 다는 것임)
타입 정보는 보통 컴파일 중에만 사용되며 컴파일러에 의해 기계어로 바뀌고 나면 사라지는 것이 일반적이다 하지만 C#은 컴파일된 결과 코드 뿐만 아니라 타입에 대한 메타 데이터를 실행 파일에 같이 기록해 놓기 떄문에 실행중에도 정보를 조사할 수 있다.
접근방법
지정된 이름으로 접근
1) Type t = Type.GetType("Test");
클래스 타입으로 접근
2) Type t = tyoeof("Test");
객체로 접근
3) Type t = Test.GetType()
프로그램의 metadata에 접근할 수 있도록 해주는 이 클래스는 System.Refelection namespace에 정의 해 주어야한다.
오류가 났다. 바로 'Global_Define' namespace를 선언해 주지 않았기 때문이다.
예시
콘솔창에서 텍스트를 출력하는 Console이 오류가 났다.
이유는 System이라는 namespace가 없기 때문이다.
Console클래스 정의로 이동해봤다.
Console이라는 클래스에는 WirteLine( )이라는 메서드가 있었다.
Console이라는 클래스는 System 네임스페이스의 정의 되어있다.
System은 -> 네임스페이스
Console은 -> System의 정의되어있는 클래스
WirteLine은 -> Console클래스 에 정의된 메소드
네임스페이스 정의방법
첫 번째
맨 윗단에 해당 using을 사용하여 C#파일에서 사용하고자 할때
두 번째
클래스명 앞에 네임스페이스 전부를 적는 경우 이 경우는 네임스페이스 안의 메소드,
변수를 사용하려면 앞에 매번 적어줘야함
네임스페이스는 즉
소속이라는 정의하는 역할을 하는 것이다.
네임스페이스를 귀찮게 나눠서 쓰는 이유.
이름이 같은 변수,메소드등등 이지만 실행되는 내용들은 다를 수 있기 떄문이다.
A프로그램과 B프로그램이 있는데 같은 이름을 사용하지만 그 내용들이 조금 다르면?
프로젝트가 커지고 스크립트 수가 증가할수록 스크립트 클래스 이름이 충돌할 가능성이 커집니다. 이것은 게임의 다른 파트를 따로 작업하고 궁극적으로 같은 프로젝트에 통합하는 경우에 적용됩니다. 예를 들어 한 명의 프로그래머가 메인 플레이어의 제어 코드를 만들고, 또 한사람이 적 캐릭터의 제어 코드를 작성했다고합니다. 둘 다 기본 스크립트 클래스를 Controller라고 명명하고 프로젝트를 통합할 때 이름이 충돌합니다.
<유니티도큐>
유니티에 생성되는 namespace
유니티에서 스크립트를 생성하면 자동으로 추가 되는 것들이다. 클래스에서 Start나 Update메소드가 자동으로 생성되지만 있는 존재만으로도 비용이 들기 때문에 사용하지 않으면 삭제한다. namespace도 마찬가지이다. 사용하지 않으면 지워주자
체스 말판이 위 특정 셀에 위치할 때 움직일 수 있는 방법의 개수를 구하는 문제이다. 특정 위치 이기 때문에 특정 셀 밑이거나 위에 있을 때 제한된다.
풀이 : 내가 작성한 코드
메모장에 적으면서
처음 생각하기에 문자열의 앞cell[0]뒤cell[1] 8방향의 이동하게 문자를 더하는 값으로 확인이 가능할까 생각했지만 안된다고 판단하고,체스 말이 위 체스 말이 움직 일 수 있는 방법이 최대 8가지이기 때문에 8방향을 모두 체크하고 각각 케이스에 검사하는 방법밖에 생각이 안 났다. 그래서 그냥 case문을..
풀이 : 다른 사람이 작성한 코드
가장 높은 vote를 받은 코드이다. 5개의 길이의 배열을 만드는데 무엇인가 했더니 체스 말판이 움직일 수 있는 방향 개수들이다. 그다음으로 문자 알파벳과 숫자를 체크하는데 'a'나 'h'를 빼는 이유는 체스가 저 두 문자에 위치했을 때 이동 방향 개수가 제한된다. 또, 체스 말판이 a에 가깝나 h에 가깝나를 체크한다. Math.Min(int,int )는 둘 중 작은 수를 반환하는 함수인데, 'a', h'에 가깝지 않아 2를 넘으면 4방향은 이동 가능이 보장이 된다. 반대로 마찬가지로 숫자 부분도 체크하여 리턴된 두 정수를 더한 뒤 배열에서 찾는다..
일단 문제 접근에 대한 방식 자체가 다르다ㅜ. 훨씬 더 깔끔하고 나도 문제에서 8가지 방향을 정해져 있다고 판단하고 포문을 8번 돌렸지만, 이 코드는 한 번 더 생각하여 제한된 이동 방향 개수 생각하고 그 값을 배열로 넣어두었다. 그리고 dist라는 말의 칸 이동 수가 정해져 있기 때문에 체스판 테두리에 가까웠을 때 제한되는 방법으로 깔끔하게 구현했다. 역시 접근 방법은 여러 가지로 생각하는 게 좋겠다.
문자열중 가장 작은 수를 빼면 가장 큰 수가 나 올 줄 알았다. 그런데 222250은 0이 가장 작은데 22225가 된다. 하지만 답은 22250이 되어야 하고 그래서 이건 아니고...,앞 자릿수보다 뒷자리수가 크면 앞 자릿수를 빼면 된다. 인덱스 비교로 현재 값보다 다음 자릿수의 값이 크면 break로 빠져나와 그 숫자를 제거한다.
풀이 : 다른 사람이 작성한 코드
string s로 처음 받아온 숫자를 문자열로 바꾼 뒤 그 문자열에 서 해당하는 문자만 빼고 int형으로 리턴했다. 나는 형 변환도 많고, 굳이 안 써도 되는 집합 자료구조는 등 많이 사용하는 게 습관이 됐는데 최대한 쓰지 않는 방법으로 문제를 풀도록 해야겠다.
public class m_test : MonoBehaviour
{
public delegate int Comparsion<in T>(T x, T y);
static int SortWithPrice(Product a, Product b)
{
return a.price.CompareTo(b.price);
}
class Product
{
public string Name { get; set; }
public int price { get; set; }
}
void Start()
{
List<Product> products = new List<Product>()
{
new Product() { Name = "감자", price = 500 },
new Product() { Name = "사과", price = 700 },
new Product() { Name = "고구마", price = 400 },
new Product() { Name = "배추", price = 600 },
new Product() { Name = "상추", price = 300 }
};
products.Sort(SortWithPrice);
foreach(var val in products)
{
Debug.Log(string.Format("품목:{0}, 가격:{1}",val.Name, val.price));
}
}
}
public delegateint Comparison<in T>(T x, T y);
T : 비교할 개체의 형식,
x : 비교할 첫 번째 개체
y : 비교할 두 번째 개체
public class m_test : MonoBehaviour
{
//public delegate int Comparsion<in T>(T x, T y);
//static int SortWithPrice(Product a, Product b)
//{
// return a.price.CompareTo(b.price);
//}
class Product
{
public string Name { get; set; }
public int price { get; set; }
}
void Start()
{
List<Product> products = new List<Product>()
{
new Product() { Name = "감자", price = 500 },
new Product() { Name = "사과", price = 700 },
new Product() { Name = "고구마", price = 400 },
new Product() { Name = "배추", price = 600 },
new Product() { Name = "상추", price = 300 }
};
products.Sort(delegate (Product a, Product b)
{
return a.price.CompareTo(b.price);
});
//products.Sort(SortWithPrice);
foreach(var val in products)
{
Debug.Log(string.Format("품목:{0}, 가격:{1}",val.Name, val.price));
}
}
}
=> SortWithPrice 메서드를 선언하지 않고도 가능하다.
products.Sort((a, b) =>
{
return a.price.CompareTo(b.price);
});
=> 람다 사용
콜백메서드 구현하기
class Monster
{
public string Name { get; set; }
public double Pow { get; set; }
public Monster(string name, double pow)
{
Name = name;
Pow = pow;
}
public override string ToString()
{
return this.Name+" : " +this.Pow;
}
}
Monster.class 몬스터이름과 파워수치를 받는 클래스
class Monsters
{
private List<Monster> listOfMonster = new List<Monster>();
public delegate void PrintProcess(Monster list);
public void Add(Monster monster)
{
listOfMonster.Add(monster);
}
public void Print()
{
Print((monseter) =>
{
Console.WriteLine(monseter);
});
}
public void Print(PrintProcess porocess)
{
//콜백메서드를 사용
foreach(var item in listOfMonster)
{
porocess(item); //콜백 메서드에 매개변수를 전달해 호출
}
}
}
스태틱 변수는 객체를 선언만 해도 메모리가 할당되며 일반적인 변수들이 객체가 새로 생성될 때 메모리가 초기화 되는 것과 다르게 해당 객체를 계속 반복적으로 생성해도 메모리가 유지 된다는 특징이 있다.
테스트를 해봤다
public class Myclass
{
private static int stNum= 0;
private int num = 0;
public Myclass()
{
stNum++;
num++;
}
public void Showtest()
{
Console.WriteLine("stNum: {0}, num : {1}", stNum, num);
}
}
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Myclass test = new Myclass();
test.Showtest();
}
}
}
일반 변수인 num의 값은 변하지 않지만 Static으로 선언한 stNum은 메모리를 초기화 하지 않고 계속 더해 지고 있다.
=> static 필드는 프로그램 실행 후 해당 클래스가 처음으로 사용될 때 한번 초기화되어
계속 동일한 메모리를 사용하게 된다.
스태틱Static 메소드
Static메소드는 객체가 메모리를 할당 받기 전에 호출이 되기 때문에 객체 생성 후 메모리 할당을 받는 일반 변수에 접근이 불가능하다. 인스턴스 메소드와 다르게 클래스로 객체를 따로 생성하지 않고 [클래스명.메서드명]이런식으로 호출하게 된다.
호출 시 일반 인스턴스 메서드와 달리 클래스로 부터 객체를 생성하지 않고 직접 호출한다.
일반 인스턴스 메서드는 클래스로부터 객체를 생성하고 메서드를 호출해야하지만
static메서드는[클래스명.메서드명]으로메서드를 호출한다.
static메소드 내부에서 객체 멤버를 호출 할 수없다.
인스턴스 메서드는 스태틱메서드 멤버를 참조가 가능하지만 스태틱메서드는 다른 멤버 참조가 불가능하다.
스태틱Static 클래스
Static 클래스는 모든 클래스 멤버가 static 멤버로 되어 있으며, 클래스명 앞에 static이 붙는다.
다른 일반 클래스와 다르게 static생성자를 갖는다. 이 생성자는 보통 static필드를 초기화 할 때 사용한다.
테스트를 해보았다.
static 멤버 호출
객체 생성 불가
static는 인스턴스화 하지 않는 클래스이기 때문에 객체생성이 불가능하다.
스태틱객체는 프로그램이 종료되기 전까지 메모리를 해제하지 않고 객체를 생성하지 않고도 멤버에 접근이 가능하다는 장점 (특징)을 잘 활용해야 할 것같다. 하지만 단점으로 여러군데서 스태틱 멤버를 동시에 참조하게 될 경우와 객체 지향에 벗어난 개념이 되어 안좋다는 시선도 있다고 한다. 다른 장단점을 알아 볼 필요가 있겠다.
참조 : http://www.csharpstudy.com/csharp/CSharp-static.aspx 사이트
http://ronniej.sfuh.tk/ , http://mygumi.tistory.com/253 ,http://blog.acronym.co.kr/347 블로그
메모리 관리 기법중 하나, 프로그램이 (동적)할당했던 메모리에서 필요없게 된 영역을 해제하는 과정이다.
명시적 해제가 필요없다
C++에서는 개발자가 직접 메모리할당을 해제를 해야하는데 유니티에서 사용하는 C#프로그래밍에서는 가비지 컬렉터가 존재하기 명시적 메모리해제를 하지 않아도 된다. 또,유니티 Mono엔진 런타임 시스템 덕분에 메모리 노출 가능성도 크게 준다고 한다.
언제 발생?
힙을 할당해야하는데 사용가능한 메모리가 충분하지 않다고 판단될때, 그리고 아무도 모르는 시점(플랫폼마다 다르다고 한다.)
알 수 없는 메모리 해제 시점
가비지콜렉터는 메모리를 해제 할때 작업을 수행하는 데 시간이 소요된다. 메모리의 양과 프로그램을 실행 중인 플랫폼에 따라 다르고, 중요한 것은 메모리가 해제되는 시점을 개발자가 알 수 없다는 것이다. 게임과 같은 실시간으로 실행되는 프로그램에서는 큰 문제가 될 수 있다.
가비지컬렉터가 실행되면 동반되는 것들
힙에 할당된 변수가 스코프를 벗어나면, 사용되었던 메모리는 이때 해제 되지 않고, 가비지 컬렉터가 실행되어야 해제 되는 이이 때 일어나는 과정들이 있다.
가비지 컬렉터가 힙 상의 모든 오브젝트를 검사하는데 오브젝트 참조값을 모두 확인해서 해당 오브젝트가 힙상에서 스코프내에 있는지 확인하여 삭제 여부를 판단하다. 당연히 힙 상의 오브젝트가 더 많을수록 가비지 컬렉터는 할 일이 많아진다.
힙 파편화
힙에 메모리가 저장될 때 데이터의 크기에 따라 블록 단위로 저장이 되는데 서로 다른 크기의 메모리 블록이 저장되고 해제되면서 남은 공간이 생기게 된다. 그렇기 때문에 실제 개발자가 사용하려했던 프로그램 메모리 사용량이 늘어나고, 메모리를 찾기 위해 가비지 컬렉터가 더 자주 실행하게 된다.
참고 : https://docs.unity3d.com/Manual/UnderstandingAutomaticMemoryManagement.html?_ga=1.214538258.405170267.1480455402