'분류 전체보기'에 해당되는 글 11건

  1. 2019.02.16 C# Boxing Unboxing
  2. 2019.01.13 C# dispose using
  3. 2019.01.09 State Pattern
  4. 2019.01.09 C# WeakReference
  5. 2019.01.01 .NET Framework
  6. 2018.12.29 C# Singleton
  7. 2018.12.29 C# static
  8. 2018.12.25 부동소수점
  9. 2018.12.24 10진수 실수를 2진수로 변환하는 방법
  10. 2018.12.19 Game Loop

값형식

 - 형식의 값이 저장됨

 - 새 값을 할당하면 개체 자체의 값이 복사됨

 - System.ValueType에서 파생됨

 - 구조체(숫자형식, bool, 사용자정의), 열거형 등의 형식 제공


참조형식

- 데이터에 대한 참조가 저장됨

- 새 값을 할당하면 개체의 참조가 복사됨

- class, interface, delegate, dynamic, object, string 등의 형식 제공


Boxing

 - 값 형식을 object(혹은 object에서 파생된) 형식으로 변환하는 프로세스

 - 암시적

 - System.Object 내부에서 관리되는 힙에 할당되고 값이 복사됨


UnBoxing

 - object(혹은 object에서 파생된) 형식에서 값 형식으로 변환하는 프로세스

 - 명시적

 - 인스턴스가 지정한 값 형식을 boxing한 값인지 확인 후 인스턴스의 값을 변수에 할당


예시코드


int valueType = 1;

object refType = valueType;

int valueType2 = (int)refType;








주의사항

 * 비용 : 최대한 피하기

  - boxing은 단순 참조보다 20배정도의 소요시간이 걸리고 unboxing의 캐스팅프로세스는 할당의 4배정도의 시간이 걸림

  - ArrayList는 boxing, unboxing을 유발하므로 가능하면 List<T>를 사용하자

  - 힙에 할당이 되었다는 것은 GC가 호출될 가능성이 있다는 것을 유의해야 함

 

* 예외 : try catch 사용, if로 조건 체크 등

  - null을 unboxing 할 수 없음 => NullReferenceException

  - 호환되지 않는 값 형식에 대한 참조를 unboxing 할 수 없음 => InvalidCastException


Posted by @히테
,

Dispose

C# 리소스

관리되지 않는 리소스 : 파일스트림, 네트워크연결, 데이터베이스연결 등 운영체제 리소스를 래핑하는 개체

관리되는 리소스 : 그 외에 리소스

* 관리되는 리소스는 가비지컬렉터가 관리해주지만 관리되지 않는 리소스는 가비지컬렉터가 관여할 수 없음

 => 관리되지 않는 리소스는 프로그래머가 직접 해제


ex) File을 읽고 Close호출(아래 예시대로 사용하면 안됨!!!)

var file = new StreamReader("file.txt");
var content = file.ReadToEnd();
file.Close();

만약 file이 null이어서 오류가 난다면 Close는 호출되지 않음



dispose

Dispose : 관리되지 않는 메모리를 명시적으로 해제

관리되지 않는 리소스는 IDisposable 인터페이스를 구현해둠

위 예제는 다음과 같이 수정할 수 있음

StreamReader file = null;
string content = string.Empty;
try
{
    file = new StreamReader("file.txt");
    content = file.ReadToEnd();
}
finally
{
    if(file != null)
        file.Dispose();
}



dispose 패턴의 구현


public class DisposableClass : IDisposable
{
    private bool disposed;

    public DisposableClass() {}
    ~DisposableClass()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this); // Finalizer를 호출되지 않게 제한하며 GC Call
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposed == false)
        {
            if(disposing)
            {
                // 여기서 리소스 해제
            }
        }
        disposed = true;
    }
}


다음과 같은 베이스에서 원하는 로직을 구현 후 StreamReader 예제와 같이 사용하면 됨




using

로직을 수행한 후 자체적으로 Dispose를 호출해주는 명령어

위 예제를 다음과 같이 축소할 수 있음

string content = string.Empty;
using(var file = new StreamReader("file.txt"))
{
    content = file.ReadToEnd();
}





Posted by @히테
,

상태패턴
 FSM을 이용해 디자인하는 패턴




FSM(Finite State Machine)
 상태기계
 - 유한한 개수의 상태를 갖을 수 있음
 - 현재 상태에서 다음 상태로 전이 가능
 - 진입, 현재, 퇴장으로 동작 구분






장점
구조가 정해져있어서 작업이 용이하고 일감 분배가 비교적 쉬움




단점
FSM틀을 벗어난 행위를 만들 수 없음





보완
FSM은 한가지 상태밖에 가질 수 없기 때문에 '걷는 동시에 공격한다' 등의 상태를 기술하려면 상태를 추가하면서 상태간 연결도 복잡해짐
이를 보완하기 위한 방법들을 설명한다

 1. 병행상태기계 : 복수개의 상태머신 사용
  - 독립된 개념을 상태로 두어 상태가 복잡해지는 것을 방지
  - 개념이 추가될 때 마다 코드가 복잡해짐
  - 상태코드 간 커플링 발생할 수 있음

ex)
 캐릭터 이동관련 상태기계
 캐릭터의 공격여부 상태기계
두개를 같이 사용! 하지만 엎드렸을 때 공격 불가 등의 로직 구현 시 커플링 발생


 2. 계층상태기계 : 상태를 상속하여 상태 만듬
  - 큰 개념을 상위의 상태로 만들고 그것을 상속해서 사용
  - 설계하기 복잡함

ex)
 필드 별로 다른 상태 만들기
 이동, 정지, 엎드리기 등 기본 움직임을 갖는 상태 => A
 A를 상속받아서 땅, 물, 하늘 등으로 분류
 땅 위 이동, 물 속 정지 등 로직 구현가능!

 
 3.푸쉬다운 오토마타 : FSM에 이전 상태에 대한 이력을 남김
  이 방법은 FSM 단점 보완보단 확장에 가깝다 
  총을 쏜 후 버튼을 떼면 기본상태로 돌아와야하는 로직, 풀을 채집하다가 완료되면 원래 상태로 돌아와야하는 로직 등을 구현
  상태를 스택으로 관리하면서 상태 관리
   - push : 새로운 상태가 top이 되면서 그 상태가 현재상태가 됨
   - pop : 현재 상태였던 최상위 상태를 빼면서 top이 되는 이전 상태가 현재 상태가 됨


샘플코드
FSMState.cs

public abstract class FSMState
{
    public Type Key { get; private set; }
    private List nextState;

    public FSMState(Type key)
    {
        Key = key;
    }

    public abstract void OnEnter();
    public abstract void OnStay();
    public abstract void OnExit();

    internal void Link(Type state)
    {
        if (nextState == null)
            nextState = new List();

        nextState.Add(state);
    }

    internal bool IsLinkable(Type state)
    {
        return !nextState.Contains(state);
    }

    internal void EnterPrco()
    {
        OnEnter();
    }

    internal void StayProc()
    {
        OnStay();
    }

    internal void ExitProc()
    {
        OnExit();
    }
}


FSM.cs


public class FSM
{
    private Dictionary> stateDic;
            private FSMState currentState = null;
    public State CurrentState
    {
        get
        {
            return currentState.Key;
        }

        private set
        {
            currentState = FindState(value);
        }
    }

    public void ChangeState(State stateName)
    {
        if (currentState != null)
        {
            currentState.ExitProc();
        }

        CurrentState = stateName;

        if (currentState != null)
        {
            currentState.EnterPrco();
        }
    }

    public void UpdateState()
    {
        currentState.OnStay();
    }

    public bool AddState(FSMState state, bool isDefaultState = false)
    {
        if (stateDic == null)
        {
            stateDic = new Dictionary > ();
        }

        if (stateDic.ContainsKey(state.Key))
        {
            return false;
        }

        if (stateDic.ContainsValue(state))
        {
            return false;
        }

        stateDic.Add(state.Key, state);

        if (currentState == null || isDefaultState)
        {
            currentState = state;
        }

        return true;
    }

    public bool Link(State from, State to)
    {
        if (from.Equals(to))
        {
            return false;
        }

        FSMState fromState = FindState(from);
        if (fromState == null)
        {
            return false;
        }
        FSMState toState = FindState(to);
        if (toState == null)
        {
            return false;
        }

        if (fromState == toState)
        {
            return false;
        }

        if (fromState.IsLinkable(to))
        {
            return false;
        }

        fromState.Link(to);
        return true;
    }

    private FSMState FindState(State key)
    {
        FSMState result = null;
        stateDic.TryGetValue(key, out result);
        return result;
    }
}



Posted by @히테
,

WeakReference란
 가비지 컬렉션이 메모리를 회수하는 것을 허락하면서 레퍼런스를 참조하고 있는 클래스
    => 약한 참조


문제점


SampleClass a = new SampleClass();
SampleClass b = a;
a = null;
GC.Collect();

 다음과 같은 코드에서 SampleClass 객체는 회수되지 않는다
    => 메모리 누수



예시

1. 게임플레이에 사용되는 미사일들을 관리하는 미사일오브젝트풀러를 만들고 미사일들을 생성


2. 싱글턴으로 제작된 업적매니저가 업적클리어 결과 출력을 위해 미사일매니저의 미사일 하나를 참조(실제로 구현은 이렇게 안함.. 예시를 위해)


3. 게임플레이가 종료되어 업적을 출력한 후 미사일오브젝트풀러를 제거


4. 가비지컬렉션이 레퍼런스를 잃은 미사일들 수거

5. 업적매니저가 참조하고 있는 미사일은 레퍼런스를 잃지 않아서 수거되지 않음




사용법

SampleClass sampleClass = new SampleClass();
WeakReference weakRef = new WeakReference(sampleClass);

if (weakRef.IsAlive)
{
    Console.WriteLine(weakRef.Target);
}

sampleClass = null;
GC.Collect(0, GCCollectionMode.Forced);

if (weakRef.IsAlive)
{
    Console.WriteLine(weakRef.Target);
}




주의사항
Target을 외부에서 참조하지 말 것


Posted by @히테
,

.Net Framework


- 마이크로소프트에서 지원하는 윈도우 프로그램 개발 및 런타임 실행 환경

- 메모리 관리, 유형 및 메모리 안전성, 보안, 네트워킹, 응용 프로그램 배포 등 서비스 제공

- C#, F#, Visual Basic 등의 언어로 사용 가능


cf) .Net Core를 사용하여 Window, MacOS 및 Linux에서 앱 실행 가능






.Net Framework 구조



출처 : https://ko.wikipedia.org/wiki/%EB%8B%B7%EB%84%B7_%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC#/media/File:Overview_of_the_Common_Language_Infrastructure.svg



1) CLR : 공통 언어 기반


- 마이크로 소프트가 구현한 CLI(Common Language Infrastructure)

- Common Language Runtime의 약자

- 언어에 종속적이지 않은 플랫폼을 제공


구성요소

- CTS (공통 타입 시스템) : .Net Framwork에서 사용되는 데이터 형식

- CLS (공통 언어 스펙) : .Net 프로그래밍 언어가 갖는 공통 언어 규약, 이를 만족해야 Managaed Code의 자격이 됨

- JIT (just-in-time compilation) : 동적 번역이라고 프로그램을 실행하는 시점에 기계어로 번역하는 기법, JVM, .NET, node.js에서 JIT 컴파일을 지원함

- VES (가상 실행 시스템) : 공통 중간 언어(CIL)을 하드웨어에 맞춰 기계어로 컴파일





2) .Net Framework 클래스 라이브러리


- CLR과 강력하게 통합된 다시 사용할 수 있는 형식의 컬렉션

- 객체 지향적


구성요소

- ASP.NET : 웹 구현을 위해 사용되는 기능 패키지
- Windows Forms : Windows GUI 응용 프로그램 개발을 위해 사용되는 기능 패키지

- WPF(Windows Presentation Foundation) : windows 데스크톱 응용 프로그램을 구축하기 위한 모델 제공

- Data and XML : .NET을 통해 만ㄷ르어진 응용 프로그램과 데이터가 연결이 잘 될 수 있도록 하는 기능 패키지

- Base Class Library(BCL) : .NET Framework를 사용하는 모든 언어가 사용할 수 있는 클래스들의 라이브러리, 파일입출력, 그래픽렌더, 데이터베이스,xml 조작 등의 기능 제공

Posted by @히테
,

C# Singleton

카테고리 없음 2018. 12. 29. 22:13

싱글턴


클래스를 하나의 인스턴스로 제한하고 글로벌한 액세스 지점을 제공하는 패턴




Sample Code

이 코드는 간략한 로직을 설명하기 위한 코드이고 구글링을 해보면 각 언어에 맞게 더 좋은 코드들이 많이 존재함



class Singleton
{
    private static object syncLock = new object();
    private static Singleton instance = null;
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncLock)
                {
                     if(instance == null)
                         instance = new Singleton();
                }
            }
            return instance;
        }
    }

    protected Singleton()
    {
    }

    public void Method()
    {
    }
}

public static void Main()
{
    Singleton.Instance.Method();
}


- instance를 하나만 사용하기 위해 싱글턴에 접근할 때 static으로 선언된 필드로 접근한다

- lazy initiallization : 사용되지 않을 때에는 instance를 생성하지 않았다가 필요로 할 때 생성한다

- 멀티쓰레드 환경에서도 작동하기 위해 instance에 접근할 때 lock 사용



cf) http://csharpindepth.com/articles/general/singleton.aspx 이 블로그에서 C#에서의 싱글턴에 대한 상세한 내용(.Net 4버전 이후 포함)을 담고있다. 아주 좋은 글





싱글턴의 장점


- 프로그램에서 한개의 인스턴스만 존재하도록 보장

- 전역적으로 접근 가능

- lazy initiallization을 통해 메모리 최적화





싱글턴의 단점


- 결국 전역변수! 싱글턴으로 제작한 코드를 수정해야할 경우 프로젝트 전체를 뒤져봐야함 => 로직 간 커플링이 생길 확률 높음

- 싱글턴에서 다른 객체의 레퍼런스를 참조하고 있다면 그 객체는 메모리에 계속 올라와 있을 확률이 있다

- 만약 알맞지 않은 곳에서 싱글턴을 사용해서 싱글턴이 메모리에서 내려와야할 때 어떤 클래스의 소멸자에서 싱글턴을 다시 호출하는 경우 다시 메모리에 올라옴

- 객체지향설계원칙 중 '개방-폐쇄 원칙'을 위배

- 멀티쓰레드 환경에서 동기화처리를 잘 해야한다



=> 싱글턴으로 인해 디버깅이 힘들어지고 로직 간 커플링이 생기고 메모리 관리가 어려워지는 경우가 생기기 때문에 꼭 필요한 경우에만 사용하자(리소스로딩, 통신모듈, 로깅, 캐시, 쓰레드풀 등등)


=> 개인적인 생각으로는 C#에서는 싱글턴 로직을 구현하는 것보다 static class를 활용하는 것이 더 좋아보인다

Posted by @히테
,

C# static

카테고리 없음 2018. 12. 29. 20:10

static 한정자


- 특정 개체가 아니라 형식 자체에 속하는 정적 멤버를 선언할 수 있음

- 클래스, 필드, 메서드, 프로퍼티, 연산자, 이벤트, 생성자와 같이 사용 가능

- 인덱서, 소멸자에 사용 불가


=> 모든 정보가 인스턴스가 아니고 클래스에 담긴다고 생각하면 됨




static method


- Non Static Method 보다 속도가 빠름

- 인스턴스의 객체 멤버 참조 불가능

- 클래스 명을 통해서 사용 


NonStaticClass.StaticMethod();

- 파라미터 전달 가능


class NonStaticClass
{
    public int nonStaticField;
    public static int staticField;
    public static void StaticMethod(int inputValue)
    {
        staticField = inputValue;
    }
}

public static void Main()
{
    NonStaticClass.StaticMethod(7);
}



static field


- 인스턴스가 아닌 클래스에 속하므로 실행될 때 한번 초기화되고 계속 동일한 메모리를 사용

- Non Static Field는 인스턴스 생성할 때 마다 인스턴스에 할당되어 생기는 반면 static Field는 한 개만 존재





static class


- 프로그램이 로드될 때 정적 생성자가 한번 호출되면서 메모리에 로드됨

- 생명주기는 프로그램이 살아있는 동안이므로 프로그램이 종료되거나 crash 발생하는 경우에 해제됨

- 클래스를 인스턴스화 할 수 없음 (new 사용 불가)

- 정적 멤버만 포함(생성자도 마찬가지)

- static 생성자는 프로그램이 로드되고 난 직후 실행됨, 보통 static field를 초기화하는 경우에 사용


static class StaticClass
{
    public static int staticField;
           
    static StaticClass()
    {
        System.Console.WriteLine(staticField);
        staticField = 5;
    }
}

public static void Main()
{
    // 0이 먼저 출력되고
    System.Console.WriteLine(StaticClass.staticField);  //여기서 5가 출력됨
}


cf) static클래스는 static 멤버와 private 생성자만 포함된 클래스와 동일한 역할



Posted by @히테
,

부동소수점

카테고리 없음 2018. 12. 25. 16:38

1. 컴퓨터가 실수를 표현하는 방법


* 고정 소수점 : 정수부와 소수부로 나누어서 표현하는 방법

 - int와 다를게 없어서 연산속도가 빠르고 시스템이 간단함

 - 표현할 수 있는 수의 크기가 작음

 - 정밀도를 높이려면 필요한 비트 수가 커짐


* 부동 소수점 : 가수부와 지수부로 나누어서 표현하는 방법

 - 넓은 범위의 숫자를 표현할 수 있음

 - 상대적으로 정밀도가 높음

 - 연산이 복잡해짐







2. 2진법으로 된 부동소수점


IEEE 754 : IEEE에서 공표한 부동 소수점 방식의 표준안




s (부호) : 첫번째 비트, 수의 부호를 결정


m (가수) : 2진수에서는 1.xxxx.. 로 이루어짐

 - 단정도(32비트) : 23비트

 - 배정도(64비트) : 52비트


e (지수) : m을 정규화 하면 정해지는 값, IEEE 754에서는 비트로 표현하는경우 e + bias가 들어가게 된다

 - 단정도(32비트) : 8비트

 - 배정도(64비트) : 11비트




* 예시) -27.625


-27.625 (10진수) = 11011.101 (2진수)


s (부호) => 

 -1 이므로 비트는 1


m (가수) => 

 - 11011.101을 4칸 이동시킴 : 1.1011101

 - 소수 부분만 남김 : 1011101

 - 나머지는 0으로 채움 : 1011101000...


e (지수) : m을 정규화 하면 정해지는 값, IEEE 754에서는 비트로 표현하는경우 e + bias가 들어가게 된다

 - 4칸 이동이므로 2 ^ 4 : e는 4

 - IEEE 754에서는 bias값은 127 이므로 4 + 127 = 131

 - 이진법 변환 : 10000011


1100000111011101000...







3. 부동소수점 오류


* 0.1을 소수점 9자리까지 출력했을 때



float value = 0.1f;
Console.WriteLine("{0:G9}", value);  //0.100000001


=> 실제로 근사치를 갖고 있음



* 0.1과 0.100000001490116119384765625을 비교했을 때


float num = 0.1f;
if(num == 0.100000001490116119384765625)
{
     Console.WriteLine("True"); // 출력
}

=> 다음과 같이 정확한 값을 갖고 있지 않아서 연산에도 오류가 생긴다


* int형으로 캐스팅



float value = 43.3f - 43.2f;
 Console.WriteLine("{0:G9}", value); //0.0999984741

 value = value * 10;
 Console.WriteLine("{0:G9}", value); //0.999984741

 int valueInt = (int)value;
 Console.WriteLine(valueInt); //0


=> 원하는 값은 0.1, 1, 1 이어야하지만 실제로 갖고있는 실수값이 근사값이기 때문에 int로 형변환 시 의도하던 값과 다른 값이 나옴








4. 오류 해결 방법


* float보다는 double 사용

 - float의 상대오차 10^-7, double의 상대오차 10^-15 

 - C# decimal : 부동소수형이 아니고 29자리까지 지원


* 바로 캐스팅하지 말고 작은 숫자만큼 더한 후 캐스팅


* 등호사용 금지(내결함성 허용) : epsilon 사용



double value = 43.3f - 43.2f;

if(value >= 0.1)
{
    Console.WriteLine("Yes!");
}
else
{
    Console.WriteLine("No!"); // No! 출력
}

if (Math.Abs(value) <= 0.1f + double.Epsilon)
{
    Console.WriteLine("Yes!"); // Yes! 출력
}
else
{
    Console.WriteLine("No!");
}

=> 최대한 실수형 간 계산을 피하기

Posted by @히테
,




27.625 를 변환해보자



1. 정수부 변환




27 = 

1  * 2^0 + 

1  * 2^1  + 

0 * 2^2 + 

1  * 2^3 + 

1  * 2^4



27 (10진수) = 11011 (2진수)



2. 소수부 변환


1이 넘는 부분은 정수부를 버림


0.625 (10진수) = 0.101 (2진수)


단 소수점을 변환할 때 무리수가 되는 경우가 있는데 적당히 잘르면 됨(다음과 같은 방법으로 8진수, 16진수로도 변환 가능)






27.625 (10진수) = 11011.101 (2진수)

Posted by @히테
,

Game Loop

카테고리 없음 2018. 12. 19. 00:25

Game Loop


게임 진행 시간을 유저입력, 프로세서속도와 디커플링




	while (true)
	{
		processInput();
		update();
		render();
	}


* 간단하지만 플랫폼과 작업량에 따라 게임 진행 시간이 바뀜





        #define MS_PER_FRAME 16

	while (true)
	{
		clock_t start = clock();
		processInput();
		update();
		render();

		Sleep(start + MS_PER_FRAME - clock());
	}


* 기준이 되는 프레임 속도 안에서 계산이 완료되면 쉬어줘서 전력효율 높임, 느려지는 게임에 대해서 대처 불가





	clock_t lastTime = clock();
	while (true)
	{
		clock_t current = clock();
		clcok_t elapsed = current - lastTime;
		processInput();
		update(elapsed);
		render();
		lastTime = current;
	}


* 현실 시간에 맞춰서 게임 진행 시간을 조절하지만 게임플레이가 불안정하고 코드가 비결정적(결과가 일정하지않음)





        #define MS_PER_FRAME 16

	clock_t lastTime = clock();
	double lag = 0.0;
	while (true)
	{
		clock_t current = clock();
		clcok_t elapsed = current - lastTime;
		lastTime = current;
		lag = lag + elapsed;
		processInput();

		while (lag >= MS_PER_UPDATE)
		{
			update();
			lag -= MS_PER_UPDATE;
		}
		render();
	}


* 느리거나 빨라도 잘 적응!


cf) 보간

 - 게임 상태 update는 거의 고정된 시간으로 일어남

 - render는 시간적 여유가 되면 일어남


   update와 render의 호출 빈도와 시간이 달라짐


 -> update(A)와 update(B) 사이에 render가 발생하면 A상태로 render하기 때문에 다음 render에서 튀어보일 수 있음



따라서 마지막줄 render의 파라미터로


lag / MS_PER_UPDATE : update(A)와 update(B) 사이에 흐르던 시간만큼 0부터 1로 정규화된 값


를 넘겨주고 그 값으로 보간한다






Posted by @히테
,