我正在Unity中编写一个简单的游戏,并自己学习C#。目前我正在进行评分系统的第一次传球。我决定用本地c#事件来做这件事。所以我的第一个想法是让Score类负责计算/保持球员得分。此类将从实现IScoreable
接口的对象接收事件。
它看起来像这样(示例代码):
public interface IScoreable {
event Action<IScoreable> Scored;
int Value { get; set; }
}
public class Score {
public void addScore(IScoreable scoreable) {
//do something with scoreable.Value
}
}
从理论上讲,它足够好,但我遇到了耦合问题:
只需使用上面的代码,Score就需要知道实现IScoreable
的所有可能对象,因此它可以订阅Scored事件。考虑到会有很多不同的对象实现这个接口 - 它可能会从代码的不同部分实例化,我没有看到任何“干净”的方式来实现这一点。
另一种选择是让每个IScoreable
对象都使用Score对象注册,但这又会产生强耦合。例如,添加另一个玩家,以及随后的另一个Score实例,需要重写所有实现IScoreable
)
这让我有两种选择(我看到)。创建某种事件管理器/聚合器。这个选项现在适合我(主观上说,我喜欢c#事件与定义它们的类有很强的联系)。
另一个选择,我倾向于使用静态事件。这需要从IScoreable
的接口切换到抽象类。这对c#单继承来说可能是一个大问题,但在这种情况下它不是(基于Unity组件的方法,不鼓励深度继承树)我认为它很适合这个用例。
public abstract class Scorable {
public static event Action<Scorable> Scored;
protected virtual void onScored() { if (Scored != null) Scored(this); }
public int Value { get; set; }
}
分数对象只会订阅评分,然后完成。从Scoreable继承的每个类都会在需要时调用base.onScore
并完成。不需要额外的课程。除了内存泄漏的可能性 - 如果不小心我没有看到这方面的缺点。
但由于静态事件的使用似乎令人沮丧,我怀疑,也许是因为我缺乏经验,我没有看到一个简单的解决方案。
所以问题是......我的问题是否有更好(更清洁,更简单)的解决方案?在这种情况下,你会建议不要使用静态事件吗?
答案 0 :(得分:1)
我认为你倒退了。您应该拥有可能更新Score
知道单个实例的所有对象,而不是让Score
对象知道可以改变分数的所有内容。单身)Score
对象。
非常简单的情况可能是这样的:
public class Score
{
public int TheScore { get; private set; }
public static _instance;
public static Instance // I'm using a static property, but you could also create a static "getInstance" method if you prefer
{
get
{
if (_instance == null)
{
// Note: if you are multithreading, you might need some locking here to avoid ending up with more than one instance
_instance = new Score();
}
return _instance;
}
}
private Score()
{
// Note: it's important to have a private constructor so there is no way to make another instance of this object
}
public int AddToScore(int score)
{
// again - if you have multiple threads here you might need to be careful
TheScore += score;
}
}
现在,您的对象可能会更新分数,您只需:
Score.Instance.AddToScore(100); // adds 100 to the score
现在,如果您想在这里真正感兴趣,可以将Score
类抽象为接口IScore
,然后使用控制反转(IoC)容器为您管理它。这样,您就可以将保存分数的类的实现与将使用它的类分离。
当然要获得目前的分数:
int currentScore = Score.Instance.TheScore;
如果您想使用事件,那么您可以查看发布者/订阅者模式,其中您基本上有一个单身人士充当您的事件的经理以及需要发布事件的每个人(即您的{{1将发布到它,你的Player
类将订阅它。
答案 1 :(得分:1)
以下几项更改将为您提供更顺畅的耦合
public interface IScoreable {
event EventHandler<IScoreable> Scored;
int Value { get; set; }
}
创建一个新的ScoreEventArgs
类,以更灵活的方式处理您的事件参数
public class ScoreEventArgs: EventArgs
{
public int Score {get;set;}
// Can put more properties here to include when the event is raised
public ScoreEventArgs(int value)
{
this.Score=value;
}
}
更改抽象类以确保根据您的需要处理事件
public abstract class Scorable {
event EventHandler<ScoreEventArgs> scored;
public event EventHandler<ScoreEventArgs> Scored;
{
add { this.scored += value; }
remove { this.Scored -= value; }
}
// onScored method now handles the object (could be player in your case) where its called from and the score
protected virtual void onScored(IScorable sender,int score)
{
if (Scored != null)
{
var e = new ScoreEventArgs(score)
Scored(sender,e);
// Could set optional this.Value += score; // to get the current score or something like that
}
}
public int Value { get; set; }
}
您的致电课程将实施该活动并将获得分数
public class Player : Scorable
{
public Player()
{
// Also can register a scored event callback
Scored+= new EventHandler<ScoreEventArgs>(PlayerScored);
}
private void PlayerScored(object sender, ScoreEventArgs args)
{
// Other logic can go here as well, this method will be called after the onScored(this, e) is called from the PlayerScored method.. You can update your UI or do other stuff here
}
public event EventHandler<ScoreEventArgs> Scored
{
add {this.Scored+=value;}
remove {this.Scored += value;}
}
private void PlayerScored()
{
// Raise the on scored event
onScored(this, new ScoreEventArgs(10));
}
}
希望这能消除一些含糊之处..