Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 静态事件的更好替代方案_C#_Events_Design Patterns_Static_Decoupling - Fatal编程技术网

C# 静态事件的更好替代方案

C# 静态事件的更好替代方案,c#,events,design-patterns,static,decoupling,C#,Events,Design Patterns,Static,Decoupling,我正在Unity中编写一个简单的游戏,并自学C#。目前我正在做评分系统的第一关。我决定用本地c#events来实现这一点。所以我的第一个想法是让分数课负责计算/保存球员的分数。此类将从实现IScoreable接口的对象接收事件 它看起来像这样(示例代码): 公共接口是可接受的{ 项目动作得分; int值{get;set;} } 公开课成绩{ 公共无效添加分数(可计分){ //用可得分的价值做点什么 } } 从理论上讲,这很不错,但我在耦合方面遇到了一个问题: 只要有上面的代码,Score就需要

我正在Unity中编写一个简单的游戏,并自学C#。目前我正在做评分系统的第一关。我决定用本地c#events来实现这一点。所以我的第一个想法是让分数课负责计算/保存球员的分数。此类将从实现
IScoreable
接口的对象接收事件

它看起来像这样(示例代码):

公共接口是可接受的{
项目动作得分;
int值{get;set;}
}
公开课成绩{
公共无效添加分数(可计分){
//用可得分的价值做点什么
}
}
从理论上讲,这很不错,但我在耦合方面遇到了一个问题:

只要有上面的代码,Score就需要知道实现
IScoreable
的所有可能对象,这样它就可以订阅已评分的事件。考虑到将有许多不同的对象实现这个接口——它可能会从代码的不同部分实例化,我看不到任何“干净”的方式来实现这个接口

另一种选择是,让每个
IScoreable
对象将自身注册到Score对象,但这同样会产生强耦合。例如,添加另一个玩家,以及随后的另一个分数实例,需要重写实现
IScoreable
)的所有类

这给我留下了两个选择(我看到了)。创建某种类型的事件管理器/聚合器。这个选项现在适合我了(主观地说,我喜欢c#中事件与定义它们的类的强连接方式)

另一种选择,我倾向于使用静态事件。这需要将
IScoreable
从接口切换到抽象类。这对于c#单继承来说可能是一个大问题,但在这种情况下不是(基于统一组件的方法,不鼓励深层继承树),我认为它非常适合这个用例

公共抽象类Scorable{
公共静态事件动作评分;
受保护的虚拟void onScored(){if(Scored!=null)Scored(this);}
公共int值{get;set;}
}
Score对象只需订阅Scored,即可完成。从ScoreTable继承的每个类都会在需要时调用
base.onScore
,然后就可以完成了。不需要额外的课程。除了内存泄漏的可能性之外——如果不小心的话,我不认为这有什么坏处

但是,由于使用静态事件似乎不受欢迎,我有我的疑问,可能是因为我缺乏经验,我没有看到一个简单的解决方案


所以问题是。。。我的问题有更好(更干净、更简单)的解决方案吗?您是否建议在这种情况下不要使用静态事件?

我认为您的做法是相反的。与其让
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
,然后使用控制反转(inversionofcontrol,IoC)容器为您管理它。这样,您就可以将持有分数的类的实现与将使用分数的类解耦

当然,要获得当前分数:

 int currentScore = Score.Instance.TheScore;

如果您想使用事件,那么您可以查看发布者/订阅者模式,其中基本上有一个充当事件管理器的单例和需要发布事件的每个人(即您的
Player
类)将发布到它,您的
分数
类将订阅它。

以下是一些更改,它们将为您提供更平滑的耦合

 public interface IScoreable {
      event EventHandler<IScoreable> Scored;
      int Value { get; set; }
    }
对抽象类进行更改,以确保事件根据需要进行处理

  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; }
}
公共抽象类Scorable{
事件处理程序评分;
公共事件处理者得分;
{
添加{this.scored+=value;}
删除{this.Scored-=value;}
}
//onScored方法现在处理调用它的对象(在您的情况下可能是玩家)和分数
受保护的虚拟void onScored(IScorable sender,int score)
{  
如果(得分!=null)
{
var e=新的ScoreEventArgs(分数)
得分(发送者,e);
//无法设置可选的this.Value+=score;//以获取当前分数或类似的值
}
}
公共int值{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));
     }
}
公共类玩家:可攻击
{
公共玩家()
{
//还可以注册记分事件回调
得分+=新事件处理程序(玩家得分);
}
私有void PlayerScored(对象发送方、ScoreEventArgs args args)
{
//其他逻辑也可以放在这里,这个方法将在从PlayerCored方法调用onCored(this,e)后调用。你可以在这里更新你的UI或做其他事情
}
公共事件事件处理程序
{
添加{this.Scored+=value;}
删除{this.Scored+=value;}
}
私人void PlayerScored()
{
//提高得分项目的得分
onScored(这是新的ScoreEventArgs(10));
}
}

希望这能消除一些歧义。

Score
需要了解所有可能实现
IScoreable
”为什么?使用接口的意义在于,您不必知道实现是什么。我认为事件不是解决问题的方法。分数班的单身学生可能是最好的选择。每个IScorable对象都将处理各自的事件,请求Score的singleton实例并调用适用的方法
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));
     }
}