Better alternative to static events - c#

I'm writing a simple game in Unity, and learning C# on my own. Currently I'm doing first pass on the scoring system. I decided to do this with native c# events. So my first idea was to have the Score class responsible for counting/keeping player score. This class would receive events from objects implementing the IScoreable interface.
It would look something like this (example code):
public interface IScoreable {
event Action<IScoreable> Scored;
int Value { get; set; }
}
public class Score {
public void addScore(IScoreable scoreable) {
//do something with scoreable.Value
}
}
In theory, it's decent enough, but I had a problem with coupling:
With just the above code, Score needs to know about all possible objects that implement IScoreable, so it can subscribe to the Scored event. Considering there will be a lot of various objects implementing this interface - it may get instantiated from different parts of code, I don't see any "clean" way of doing this.
Another option would be, to have every IScoreable object register itself with Score object, but this again would create strong coupling. For example adding another player, and subsequently another Score instance, would require rewriting all classes implementing IScoreable)
Which leaves me with two alternatives (that I see). Create some kind of event manager/ aggregator. This option is now for me (subjectively speaking, I like how in c# events are strongly connected to class that defines them).
Another option, the one I'm leaning towards, is to use a static event for this. This would require a switch from interface to abstract class for IScoreable. Which could be a big deal with c# single inheritance, but in this case it isn't (Unity component based approach, discourages deep inheritance trees) I think it would fit this use case quite well.
public abstract class Scorable {
public static event Action<Scorable> Scored;
protected virtual void onScored() { if (Scored != null) Scored(this); }
public int Value { get; set; }
}
Score object would simply subscribe to Scored, and be done. Every class inheriting from Scoreable would call base.onScore when needed, and be done. No additional classes would be needed. Other than possibility of memory leak - if not careful I don't see downsides to this.
But since the use of static events seems to be discouraged, I have my doubts, maybe because of my inexperience I don't see a simple solution.
So the question... Is there a better (cleaner, simpler) solution to my problem? Would you advise against the use of static event in this case?

I think you have this backwards. Rather than have a Score object that knows about everything that can change the score, you should have all of your objects that might update the Score know about a single instance (a singleton) Score object.
Really simple case might be something like:
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;
}
}
Now in your object that might update the score, you just:
Score.Instance.AddToScore(100); // adds 100 to the score
Now if you want to get really fancy here, you could abstract the Score class into an interface IScore and then use an inversion of control (IoC) container to manage it for you. That way you can decouple the implementation of the class that holds the score from the classes that will consume it.
And of course to get the current score:
int currentScore = Score.Instance.TheScore;
If you want to use events, then you could look at the publisher/subscriber pattern where you'd basically have a singleton that acts as the manager for your events and everybody that need to publish an event (i.e. your Player class) will publish to it and your Score class will subscribe to it.

Here are a few changes that will give you much smoother coupling
public interface IScoreable {
event EventHandler<IScoreable> Scored;
int Value { get; set; }
}
Create a new ScoreEventArgs class to handle your event args in a more flexible way
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;
}
}
Changes to your abstract class to ensure that event is handled according to your needs
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; }
}
Your calling class which will implement the event and will have a score
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));
}
}
Hope this clears some ambiguities..

Related

Is there a way to catch a method call without subscribing it to any sort of Events?

suppose we have this scenario :
a class that you are not allowed to modify anything in it :
public class ForbiddenClass_A
{
public void TheMethod()
{
//do stuff
}
}
and another read only class that calls a method from the previous class:
public class ForbiddenClass_B
{
ForbiddenClass_A fc_a;
void Update()
{
//some logic that if true it will call :
fc_a.TheMethod();
}
}
Now you have your class, that you do anything to it, and from it you want to know if TheMethod() :
public class MyClass
{
//call this when TheMethod() from ForbiddenClass_A is called.
public void TheMethod_Catcher()
{
}
}
Thank you!
Is there a way to catch a method call without subscribing it to any
sort of Events?
Decoupled messaging is probably where you want to be, event aggregator or any other pub sub method messaging system. Although you still have to subscribe to something, the participants need not know about each other allowing you to make the methods private.
Unity, MvvmLight both have these sorts of messaging systems, however they are truly dime-a-dozen, there are plenty
Example of how this might work
public CreateUserForm()
{
InitializeComponent();
EventPublisher.Instance.Subscribe<NewUserCreated>
(n => listBoxUsers.Items.Add(n.User.Name));
}
...
// some other class
private void Update()
{
var user = new User()
{
Name = textBoxUserName.Text,
Password = textBoxPassword.Text,
Email = textBoxEmail.Text
};
EventPublisher.Instance.Publish(new NewUserRequested(user));
}
Update
There are injection techniques if you are interest for .net
Dynamically replace the contents of a C# method?

What is wrong with this design of RPG game effect system or how may I remove all particular effects from a list of different lists of derived effects?

I guess I've run into a design issue... which I'm not sure how to solve myself. So sorry, but I have to provide some basic design info...
A game. Players, monsters, objects, different things may be under different effects:
public interface IEffect {}
There may be different particular Effects:
public interface IResistanceBuff : IEffect
{
void modifyIncomingDamage(Damage damage);
}
public interface IDamageBuff : IEffect
{
void modifyOutgoingDamage(Damage damage);
}
Why this way? Well, the concept it to avoid duplicating code. For example, this way I can unify damage reductions from type resistances and, for example, wearing armor. Simply, I make the Type class and the Armor class implement IResistanceBuff. (Similarily, the Weapon class will implement IDamageBuff).
But some effects are temporary, not permanent. For this, we will have:
public interface ITemporaryEffect : IEffect
{
long TimeRemaining {get; set;}
}
Again, the purpose here is to avoid duplicating code. For example, the PotionOfMight class will implement IDamageBuff interface and derive from Potion abstract class, which in turn will implement ITemporaryEffect interface. Or, we will have IStun interface, which will implement ITemporaryEffect.
Now every object that can be under certain effects will store a collection (HashSet maybe?) of all effects it is under. This is to be able to more easily call these effects. For example, a character will have a property public HashSet<IResistanceBuff> ResistanceBuffs {get; private set;} and so we can make a character's TakeDamage method look like this:
void TakeDamage(Damage damage)
{
this.ResistanceBuffs.ForEach(resistanceBuff =>
resistanceBuff.modifyIncomingDamage(damage)
);
this.HP -= damage.Amount;
if(this.HP <= 0) {
this.Faint();
}
}
But here comes the problem. I want to have one piece of code to update the remaining duration of temporary effects and remove those whose duration has expired.
My (not working, by design of C#) idea for that was to make the Character class (and any other class that represents an object that can be under any sort of Effect - that means even WorldMap, because I thought I'd store weather conditions as Effects applied to the world) implement IEffectStorage interface:
public interface IEffectStorage
{
List<HashSet<IEffect>> AllEffects {get;}
}
public class Character : IEffectStorage
{
// ...
public HashSet<IResistanceBuff> ResistanceBuffs {get; private set;}
public HashSet<IDamageBuff> DamageBuffs {get; private set;}
// ...
public List<HashSet<IEffect>> AllEffects =>
new List<HashSet<IEffect>> {ResistanceBuffs, DamageBuffs, /* ... */};
}
Now dealing with temporary effect expiration would be the responsibility of the Effect static class:
public static class Effect
{
public static void ExpireTemporaryEffects(IEffectStorage effectStorage)
{
effectStorage.AllEffects.ForEach(effectSet =>
{
var effects = effectSet.ToList();
effects.ForEach(effect =>
{
if(effect is ITemporaryEffect) {
effect.TimeRemaining--;
if(effect.TimeRemaining <= 0) {
effectSet.Remove(effect);
}
}
});
});
}
}
But, of course, as I said, I cannot do this.
Now I may start looking for some hackish and/or ugly ways to make this design working. ALtervatively, I may reason that I'm likely trying to do something against the design of the C# programming language and/or OO programming paradigm because of my failure to understand this paradigm well enough... Especially since this is the first project of this size I'm doing on my own, I choose to do the latter... So I'm asking this question :) Is my design all wrong and if yes, then how to fix it?
Sorry for the length of this post, but I feel all information above is necessary.
I am not a game developer or have experience with game engines but I think most games or engines use something like frames as a meassure for time (maybe I am totally wrong on this). My Idea would be to change your ITemporaryEffect to
public interface ITemporaryEffect : IEffect
{
bool IsActive(long currentFrame)
}
And implement it as follows:
public class SilencedEffect : ITemporaryEffect
{
private long duration = 1000;
private long endFrame;
public SilencedEffect(int currentFrame)
{
endFrame = currentFrame + duration;
}
public bool IsActive(long currentFrame)
{
return endFrame > currentFrame;
}
}
This way you could iterate over it in your character or worldmap class and remove it if isn't is active anymore.
public class Character
{
private IList<ITemporaryEffect> temporaryEffect;
public void UpdateEffects(long currentFrame)
{
var outdatedTempEffects = temporaryEffect.Where(p => !p.IsActive(currentFrame));
temporaryEffects.RemoveRange(outdatedTempEffects);
}
}

Why I cannot reach non-persistent listeners count from my UnityEvent?

I need to know how many non-persistent listeners were added to a UnityEvent.
It seems that there is no methods giving me this number in UnityEvent class. I can only get the persistent listeners count (the ones added from editor or with UnityEvent.AddPersistentListener() ).
The only solution I have figured out is to create a child class and override the function with a count of registrations.
Here is a short code showing the problem :
private UnityEvent myEvent;
private void MyFunctionToHook()
{
// func logic
}
private void MyInitialisation()
{
myEvent.AddListener(MyFunctionToHook);
myEvent.AddListener(MyFunctionToHook);
myEvent.AddListener(MyFunctionToHook);
// here, I need to know how much hooks were added.
// ??
}
1) Do you have any ideas of how I can manage to track this information natively?
2) Am I doing this right ? Is it wrong wanting to know this information ?
It seems weird to not have access to this info, because it could be useful in unit-tests / warnings..etc
Thanks for your time.
Here is the unityEvent documentation : https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html
[EDIT]
the only solution I have found so far is the following :
public class TrackingUnityEvent<T0> : UnityEvent<T0>
{
private int nonPersistentListenersCount;
public int GetNonPersistentEventCount()
{
return nonPersistentListenersCount;
}
public void AddNonPersistantListener(UnityAction<T0> call)
{
AddListener(call);
nonPersistentListenersCount++;
}
public void RemoveNonPersistantListener(UnityAction<T0> call)
{
RemoveListener(call);
nonPersistentListenersCount--;
}
}
This way is gross because you can't tell if "RemoveListener()" and "AddListener()" are successfull as they are void.
Why don't you wrap the event inside another class without inheritance, so that you can count the listener? You can also add more code to the event...
public class MySpecialEvent {
private UnityEvent event;
public EventCount { get; private set; }
public void AddListener(UnityAction call) {
event.AddListener(call);
EventCount++;
}
public void RemoveListener(UnityAction call) {
event.RemoveListener(call);
EventCount--;
}
}

Delegation vs exposing properties

i was looking at facade-like delegation in classes, for example let's suppose that you got the Equipment class ( with Equip() and UnEquip() methods), and then you have a Player class which has a reference to the Equipment.
The way i would implement the Player class is to have some public methods like EquipItem and UnEquipItem, and those methods will delegate the tasks to the Equipment class. Now if the player also has another class that does stuff, like movement, then i would put again some public methods on the Player class that delegates to the Movement class, in a facade-like way. Now one of the disadvantages of the facade pattern is the growing interface the more classes or systems it handles. And let's suppose that i know that in the future, the Player class will have alot more functionality.
I don't know if this is the correct approach, nonetheless i was thinking if it would be possible to expose the Equipment and Movement references as properties, like so:
Class Player
{
public Equipment equipment { get; set; }
public Movement movement { get; set; }
}
Now, i know that exposing the object's state is bad, but this way you have a better control of the growing interface of the class, and have more focused methods on the Player class. So instead of:
player.Equip();
you can do
player.equipment.Equip();
Sooo finally, my question would be what is the best approach in this scenario, or maybe i got something wrong ?. Thanks
PS: the example is from a game area, but the solution doesn't have to be necessarily applicable to games, i just thought it was simple to understand.
An alternate approach would be to develop a set of command actions that can be applied to the player which manipulate the state. While the command objects would have extended access to the player, the outer API would keep the implementation details as a black box.
As an example:
public abstract class Action
{
public abstract void ActOn(Player player);
}
public class EquipAction : Action
{
private Item item;
public EquipCommand(Item item) {
this.item = item;
}
public void ActOn(Player player) {
player.Equipment.Add(item);
}
}
public interface IPlayer
{
void PerformAction(Action action);
}
public class EquipmentSet
{
public List<Item> Items { get; private set;}
}
public class EquipmentManager
{
public Add(Item item) {
}
public List<Item> Items { get; }
}
public class Player : IPlayer
{
public EquipmentManager manager;
public PerformAction(Action action) {
action.ActOn(this);
}
public List<Items> Equipment {
return manager.Items;
}
}
Now obviously, there's a lot more that should be done to make this fully capable to do what you want, but the idea would be to limit your public interface to player to the structure that you want to expose (e.g. attributes, equipment, status, .etc) and delegate all the verbs to actions.
Under the covers, the actual functionality of a Player could be broken up into as many components as would make sense to limit the size of each individual components.

Action<T> equivalent for properties

I have a class that instantiates two classes which implement interfaces. I want one class to notify another class that something is OK. I could do it with an Action and then use private variables in the class but wondered if there was a direct way of doing it with properties so that when a property's value changes it updates a property on another class.
For example:
public class MyClass
{
public ILogger Logger {get;set;}
public ILogic Logic {get;set;}
private Form MyWinform;
public void Setup()
{
MyWinform = new MyWinform();
MyWinform.StartBatch += Logger.CreateFile; //Create file when user presses start
//How can I set a property on ILogic to be AllOk once ILogger says so??
//I could use an Action so that once all is ok I call IDecidedAlOK in ILogger which
//can then assign a private bool variable inside the class
Logic.ItsOKMethodSoSetVariableToTrue = Logger.IDecidedAllOKMethod;
}
public void DataIn(string Value)
{
Logic.DataIn(Value);
}
public void CreateInstances()
{
Logger = new FileLogger();
Logic = new MyLogic();
}
}
public class MyLogic : ILogic
{
public void DataIn(string Value)
{
//I want to check that all is ok before I do anything
//if (!AllOK)
//return;
//Do stuff
}
}
Implement INotifyPropertyChanged interface and subscribe to PropertyChanged event
I feel like it might be a bit more conventional to have your ILogger interface expose something like a "FileCreated" or "Ready" event, and allow your application to handle that event in order to update the ILogic object (or do whatever else is necessary).
EDIT: my apologies, after re-reading the question, I think I misunderstood what you were asking for.
There isn't any "natural" object that does exactly what you're asking, but you could create an anonymous delegate (or lambda expression) for this purpose:
Action<bool> loggerResult = (value) => Logic.ItsOKMethodSoSetVariableToTrue = value;
A property internally consists of two private methods, a get_XXX and a set_XXX, so unless you want to fetch the MethodInfo of those methods and invoke them (which are again methods) you have no choice but to implement a method calling approach.
Subscribing to event (INotifyPropertyChanged or some custom one) is OK, so is the method to pass a lambda-setter, but in some cases it might be more convinient to use a shared context object (much like the shared memory concept):
class ConversationContext
{
public bool EverythingIsOK { get; set;}
}
This object is passed to all interested objects (ILogic and ILogger) and they operate directly on it, instead of some internal properties. If change notifications are required, Implement INotifyPropertyChanged on it.
One positive aspect of this approach is that you won't get tangled in repeatedly firing events triggering other events and so on. A single object will hold the current state and no recurrent updates are needed.
Again, this is just one of many options.

Categories

Resources