Delegate = 대리자
말그대로 대신 실행시켜주는것
invoke나 event를 쓰기위해선 필수
그외에도 비동기 계열들을 동기 계열처럼 쓸 수 있게 해준다
예제1
namespace namespace1
{
public delegate void delegate_sender(string text_buff);
public static class class1
{
void Main()
{
class2 cl = new class2(); //클래스 생성
cl.delegate_handle = new delegate_sender(text_set); //대리시킬 함수 명시
}
set_text(string text)
{
messagebox.show(text);
}
}
class class2 //이벤트를 실행할 클래스2
{
public static delegate_sender delegate_handle;
void main()
{
delegate_handle("test");//델리게이트 동작
}
}
}
이벤트
용도: 이벤트가 발생하면 이벤트 리스너에 등록된 곳에서 모두 동작을 실시합니다
namespace namespace1
{
public delegate void delegate_sender(string text_buff);
public static class class1
{
void Main()
{
class2 cl = new class2(); //클래스 생성
cl.event_handle += new delegate_sender(show_text); //이벤트 등록
}
show_text(string text)
{
messagebox.show(text);
}
}
class class2 //이벤트를 실행할 클래스2
{
public event delegate_sender event_handle; //이벤트 리스너 생성
void main()
{
event_handle("test");//이벤트 동작
}
public void CallEvent(string text)//이벤트 핸들은 반드시 해당 클래스에서 호출해야 하기 때문에 외부에서 호출시 이런식으로 호출
{
event_handle("test");//이벤트 동작
}
}
}
Invoke함수
존나 옛날에 내가 다 정리해놨었네;
요새 프로그래밍 다시 시작하면서 복습중이다
Invoke(Delegate)
//컨트롤의 내부 창 핸들이 있는 스레드에서 지정된 대리자를 실행합니다.
Invoke(Delegate, Object[])
//컨트롤의 내부 창 핸들이 있는 스레드에서 특정 인수 목록을 사용하여 지정된 대리자를 실행합니다.
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox_log.InvokeRequired)
{
//delegate_sender d = new delegate_sender (SetText);
//this.Invoke(new delegate_sender (SetText), new object[] { text });
//this.Invoke(new Action(delegate () {할일 }));
//string _return ="";
//this.Invoke(new Action(delegate () { _return = this.textBox.Text; }));
this.Invoke(new delegate_sender(SetText), text);
}
else
{
this.textBox.Text = text;
}
}
이건 윈폼예제
간단한 요약
멀티스레드에서 가장 문제가 되는 클립보드 접근에 대한것
핸들이 static이므로 그냥 막 쓸수 있다
public delegate void de_set_clip(string text); //입력예제
public delegate string de_get_clip(); //출력예제
public partial class Form1: Form
{
public void set_clip(string input) //입력함수
{
this.Invoke(new Action(delegate () {
Clipboard.SetText(input);
}));
}
public string get_clip() //출력함수
{
String _return ="";
this.Invoke(new Action(delegate () {
_return=Clipboard.GetText();
}));
return _return;
}
public Form1() //사용할 폼
{
clip.de_set_clip_handle = new de_set_clip(set_clip);
clip.de_get_clip_handle = new de_get_clip(get_clip);
InitializeComponent();
}
public static class clip
{
public static de_set_clip de_set_clip_handle; //이걸 다른 폼에서 사용
public static de_get_clip de_get_clip_handle;
}
}
존나 옛날에 짠거들이라 리메이크 해야할듯
delegate기반 계열로 Action이 있다
위쪽이 이벤트 기반이라면 이쪽은 비동기 리턴에 사용
비동기 리턴 예제
public void Login() //외부에서 리턴안쓰고 호출할때 사용
{
Login(success=> { });
}
public void Login(System.Action<bool> successCallback)
{
if (로그인 하는데 성공)
{
successCallback.Invoke(true);
}
else
{
successCallback.Invoke(false);
}
}
public void Start()
{
Login(success =>
{
if (success)
{
//성공했을경우 처리
}
else
{
//실패했을경우 처리
}
});
}
이벤트 핸들러 예제
public event System.EventHandler StateChanged;
public void OnStateChanged()
{
//sender는 누가 발생시켰는지, System.EventArgs.Empty는 리턴밸류를 할당하지 않겠다는 뜻
StateChanged?.Invoke(sender:this, System.EventArgs.Empty);
Debug.Log("상태가 전환됨");
}
public void Start()
{
StateChanged+=OnStateChanged;
}
유니티 전용 이벤트가 있다
유니티 이벤트 기능
public class CustomInteface
{
[System.Serializable]
public class Vector3Event : UnityEngine.Events.UnityEvent<Vector3> { }
//public class VoidEvent : UnityEngine.Events.UnityEvent { }
//public UnityEvent OnEvent; //void 타입
public Vector3Event OnEvent;
void Start()
{
//함수를 붙일때
OnEvent.AddListener (Draw);
//호출
OnEvent.Invoke(transform.position);
}
//붙일함수
void Draw(Vector3 pos)
{
Debug.Log ("pos:"+pos);
}
}
MonoBehaviour.Invoke
특정 함수를 0.4초 딜레이후에 호출
코루틴과 달리 오프된 상태에서도 사용가능해서 트리거 만들때 유용해보인다
유니티 함수도 쓸수있다
Invoke(nameof(Logout),0.4f);
위랑 같은데 일정시간마다 반복함
커스텀 업데이트만들때 유용해보이나 중단점을 잘 맞춰야 할것이다
InvokeRepeating(nameof(methodName),delay : 3,repeatTime : 1);
CancelInvoke(nameof(methodName)); //해당 Invoke메소드를 중단
CancelInvoke(); //모든 Invoke메소드를 중단
-코루틴 계열들
IEnumerator = 열거자
코루틴은 IEnumerator기반으로 동작함
코루틴이 동작중인지 체크
IEnumerator preCoroutine;
void StartMusic()
{
if (preCoroutine != null)
{
StopCoroutine(preCoroutine);
}
preCoroutine = MusicEnd();
Debug.Log("음악시작");
StartCoroutine(preCoroutine);
}
IEnumerator MusicEnd()
{
yield return new WaitForSeconds(10f);
Debug.Log("음악이 종료됨");
preCoroutine =null;
}
코루틴이 동작중인지 체크2
위 1번이 그냥 일반상황에서 쓴다면
2번은 코루틴내의 코루틴을 동작시킬때 씀
v1
IEnumerator preReset;
void Reset()
{
if ((preReset == null)||((preReset.Current.GetType()==typeof(int)) &&((int)preReset.Current == 0)))//Current의 타입검사도 필요함
{
preReset = ResetCo();
StartCoroutine(preReset);
}
}
IEnumerator ResetCo()
{
yield return 0;//종료
}
v2
IEnumerator preReset;
void Reset()
{
if ((preReset == null) || ((preReset.Current is int current) && (current == 0)))//Current의 타입검사도 필요함
{
preReset = ResetCo();
StartCoroutine(preReset);
}
}
IEnumerator ResetCo()
{
yield return 0;//종료
}
v3 일반
IEnumerator preReset;
void Reset()
{
if (preReset?.Current as string == "End")//Current의 타입검사도 필요함
{
preReset = ResetCo();
StartCoroutine(preReset);
}
}
IEnumerator ResetCo()
{
yield return "End";//종료
}
v3 코루틴
이 버전의 강점은 지역변수만으로도 체크 가능하단것
IEnumerator RoutineAllAria()
{
var preAria = aria;
for (int i = 0; i < System.Enum.GetValues(typeof(Aria)).Length; i++)
{
var co = Routine((Aria)i);
StartCoroutine(co);
yield return new WaitUntil(()=>co?.Current as string == "end"); //Current의 타입검사도 필요함
}
aria = preAria;
}
IEnumerator Routine(Aria aria)
{
//실행로직
yield return "end";//종료
}
v3 리스트
스킬관리할때 썼음
public void Skill(int index)
{
var delaySkillSpawn = DelaySkillSpawn(skillPrefab);
StartCoroutine(delaySkillSpawn);
delaySkillSpawns.Add(delaySkillSpawn);
delaySkillSpawns = delaySkillSpawns.FindAll(x => (x?.Current as string == "End")==false);
}
public List<IEnumerator> delaySkillSpawns = new List<IEnumerator>();
public void SkillCancel()
{
delaySkillSpawns = delaySkillSpawns.FindAll(x => (x?.Current as string == "End") == false);
foreach (var delaySkillSpawn in delaySkillSpawns)
{
StopCoroutine(delaySkillSpawn);
}
delaySkillSpawns.Clear();
}
private IEnumerator DelaySkillSpawn(SkillSystem.SkillPrefab skillPrefab)
{
yield return new WaitForSeconds(skillPrefab.spawnDelay);
if (hp<0)
{
yield return "End";//종료
yield break; //반환하지않고 중단
}
//동작코드들
yield return "End";//종료
}
frame이 10 이상이 될때까지 대기
IEnumerator Example()
{
yield return new WaitUntil(() => frame >= 10);
Debug.Log("프레임이 10 이상임");
}
비동기 함수가 끝날때까지 대기
v1
IEnumerator LoadSpritesCo()
{
{
var co = GetFolderToSpriteFilesCo(folderPath);
StartCoroutine(co);
yield return new WaitUntil(() => co.Current.GetType() == typeof(Sprite[]));
sprites = (Sprite[])co.Current;
}
}
v2
IEnumerator LoadSpritesCo()
{
{
var co = GetFolderToSpriteFilesCo(folderPath);
StartCoroutine(co);
yield return new WaitUntil(() => co?.Current as Sprite[] != null);
sprites = (Sprite[])co.Current;
}
}
IEnumerator 내부에서 코루틴 종료
IEnumerator SampleCo()
{
Debug.Log("코루틴 실행중");
yield break;
Debug.Log("코루틴이 종료되어 실행할수 없는 문구");
}
IEnumerator 반환값 예제
IEnumerator TestCo()
{
//반환값 null
yield return new WaitForSeconds(10);
//반환값 UnityEngine.WaitForSeconds
yield return 0;
//반환값 0
yield break;
//반환값 0
}
변수가 변경될경우 이벤트 호출
public IEnumerator IndexChengedChecker()
{
while (true)
{
var indexPrevious = index;
yield return new WaitUntil(() => index != indexPrevious);
//상태 변경됨
//event.Invoke();
}
}
그외에 이것들을 쓴다는건 비동기계열을 다룬다는 소리인데
스레드나 태스크계열도 참조할 필요가 생긴다 (링크참조)
'Unity > C#' 카테고리의 다른 글
C# 시리얼 (0) | 2021.10.11 |
---|---|
c# 해쉬테이블 (0) | 2021.09.13 |
자주쓰는 c# 소켓 통신 모음 (0) | 2021.09.08 |