[英]how to make an Action that points to another Action C#?
我在C#中遇到了一些意外行為。 我基本上是想將一個動作分配給另一個動作的引用 ,以便以后可以對所引用的動作進行訂閱/取消訂閱方法。 我不想知道實現引用動作的類。
問題是,應該指向我想聽的動作的動作似乎並沒有真正指向它。 我以為只要提到引用的動作,它就會增加,但是顯然不是這樣。
我可能對代表有些誤解。 有人可以告訴我我在做什么錯嗎? 我要達到的目標是否有解決方案?
感謝您的任何回應!
public class WaitForActionProcess : IProcess
{
public Action Finished { get; set; }
Action actionHandler;
public WaitForActionProcess(ref Action action)
{
actionHandler = action;
}
public void Play()
{
actionHandler += RaiseFinished;
}
public void RaiseFinished()
{
actionHandler -= RaiseFinished;
if(Finished != null)
{
Finished();
}
}
}
使用示例:
public class ReturnToMainMenuFrame : TutorialEventFrame
{
[SerializeField]
TutorialDialogueData dialogueData;
[SerializeField]
PointingArrowData arrowData;
[SerializeField]
TutorialDialogue tutorialDialogue;
[SerializeField]
PointingArrow arrow;
[SerializeField]
MainView mainView;
public override void StartFrame()
{
frameProcesses.Add(new ShowPointToUIProcess(arrow, arrowData));
frameProcesses.Add(new ShowDialogueProcess(tutorialDialogue, dialogueData));
frameProcesses.Add(new WaitForActionProcess(ref mainView.OnViewShown));
frameProcesses.Add(new HideDialogueProcess(tutorialDialogue, this));
frameProcesses.Add(new HidePointToUIProcess(arrow,this));
base.StartFrame();
}
}
框架基礎實現:
public class TutorialEventFrame : MonoBehaviour {
public delegate void OnFrameEnded();
public event OnFrameEnded FrameEnded;
public List<IProcess> frameProcesses = new List<IProcess>();
public bool debugMode = false;
public virtual void StartFrame()
{
StartProcess(0);
}
void StartProcess(int processIndex)
{
if (processIndex < frameProcesses.Count)
{
int nextProcessIndex = processIndex + 1;
frameProcesses[processIndex].Finished += () => StartProcess(nextProcessIndex);
}
else
{
EndFrame();
return;
}
if (debugMode)
{
Debug.Log("Starting process: " + frameProcesses[processIndex] + " of processes: " + (processIndex + 1) + "/" + (frameProcesses.Count - 1) + " on frame: " + name);
}
frameProcesses[processIndex].Play();
}
public virtual void EndFrame() {
foreach (var process in frameProcesses)
{
process.Finished = null;
}
if (debugMode)
{
Debug.Log("frame: " + name + " finished");
}
frameProcesses.Clear();
if (FrameEnded != null) {
FrameEnded();
}
}
}
假設OnViewShown是一個事件,並且您想在調用Play時開始偵聽此事件,然后在事件觸發時調用Finished,則可以執行以下操作:
public class WaitForActionProcess : IProcess
{
public Action Finished { get; set; }
Action<Action> Subscribe { get; }
Action<Action> Unsubscribe { get; }
public WaitForActionProcess(Action<Action> subscribe, Action<Action> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
public void Play()
{
Subscribe(RaiseFinished);
}
public void RaiseFinished()
{
Unsubscribe(RaiseFinished);
Finished?.Invoke();
}
}
稱為:
var wfap = new WaitForActionProcess(
h => mainView.OnViewShown += h,
h => mainView.OnViewShown -= h);
請注意,您要100%確保在播放后觸發OnViewShown,否則將永遠不會調用Finished,並且您也將發生內存泄漏,因為您永遠不會取消訂閱該事件。
盡管在Unity中可能無法使用它,但是請查找System.Reactive,它使這種事情形式化,並使事件處理變得更易於管理。
如何做出指向另一個動作C#的動作?
你不能。 動作是代表,代表是不可變的。
應該指向我想聽的動作的動作似乎並沒有真正指向它。
那是因為delegates
是不可變的。 即使您傳遞了委托的ref
,您在執行分配時也會創建一個副本。 代表就像strings
一樣。
public WaitForActionProcess(ref Action action)
{
// assignment creates a copy of a delegate
actionHandler = action;
}
public class Program
{
static Action action1;
static Action actionHandler;
public static void Main()
{
WaitForActionProcess(ref action1);
}
public static void WaitForActionProcess(ref Action action)
{
// this is still a reference to action1
action += Both;
// assignment creates a copy of a delegate
actionHandler = action;
// action is still a reference to action1
// but actionHandler is a *copy* of action1
action += OnlyAction1;
actionHandler += OnlyActionHandler;
action();
// Both
// OnlyAction1
actionHandler();
// Both
// OnlyAction2
}
public static void Both()=> Console.WriteLine("Both");
public static void OnlyAction1() => Console.WriteLine("OnlyAction1");
public static void OnlyActionHandler() => Console.WriteLine("OnlyActionHandler");
}
請改用List<Action>
。 這里是小提琴 。
using System;
using System.Collections.Generic;
public class Program
{
static List<Action> action1 = new List<Action>();
static List<Action> actionHandler;
public static void Main()
{
WaitForActionProcess(action1);
}
public static void WaitForActionProcess(List<Action> action)
{
action.Add(Both);
// assignment passes a reference to the List
actionHandler = action;
action.Add(OnlyAction1);
actionHandler.Add(OnlyActionHandler);
// now things work nicely
foreach(var a in action) a();
foreach(var a in actionHandler) a();
}
public static void Both()=> Console.WriteLine("Both");
public static void OnlyAction1() => Console.WriteLine("OnlyAction1");
public static void OnlyActionHandler() => Console.WriteLine("OnlyActionHandler");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.