값형식

 - 형식의 값이 저장됨

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

 - 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 @히테
,