I have classes Room and Iceball. Iceball and IceShard are inherited from class Magic.
When I add new magic to room, room subscribes to event CreateNewMagic magic, but after exiting from method event is still null.
Iceball after collision should creates ice shards in the room with the help of event, but because the event is null room cant spawn ice shards
Class Room
{
public readonly List<Magi> MagicInRoom;
public void SpawnMagic(Magic magic)
{
MagicInRoom.Add(magic);
magic.CreateNewMagic += SpawnMagic;
}
}
Class Magic, IceBall and IceShard
public abstract class Magic
{
public delegate void MagicHandler(Magic magic);
public event MagicHandler CreateNewMagic;
public virtual void OnCollisionEnter()
{
if (...)
{
...
}
}
}
public class IceBall : Magic
{
public delegate void MagicHandler(Magic magic);
public event MagicHandler CreateNewMagic;
public override void OnCollisionEnter()
{
if (...)
{
var iceShards = CreateIceShards();
foreach (var iceShard in iceShards)
if (CreateNewMagic != null) //ALWAYS IS NULL
CreateNewMagic(iceShard);
}
}
}
public class IceShard : Magic
{
...
}
Event should be abstract
public abstract class Magic
{
public delegate void MagicHandler(Magic magic);
public abstract event MagicHandler CreateNewMagic;
public virtual void OnCollisionEnter()
{
if (...)
{
...
}
}
}
public class IceBall : Magic
{
public override event MagicHandler CreateNewMagic;
public override void OnCollisionEnter()
{
if (...)
{
var iceShards = CreateIceShards();
foreach (var iceShard in iceShards)
if (CreateNewMagic != null) //ALWAYS IS NULL
CreateNewMagic(iceShard);
}
}
}
public class IceShard : Magic
{
public override event MagicHandler CreateNewMagic;
}
Related
I have this simple interface:
public interface IEventPublisher<T>
{
public delegate void EventHandler(T report);
public event EventHandler OnEventReceived;
}
Then I can write a class to subscribe to events of type int and string:
public class Subscriber
{
public Subscriber(IEventPublisher<int> intPublisher, IEventPublisher<string> stringPublisher)
{
intPublisher.OnEventReceived += OnIntEventReceived;
stringPublisher.OnEventReceived += OnStringEventReceived;
}
private void OnIntEventReceived(int report)
{
}
private void OnStringEventReceived(string report)
{
}
}
Now comes the challenge: I would like to write a class to be a publisher of events of type int and string, so I start with:
public class Publisher : IEventPublisher<int>, IEventPublisher<string>
{
}
How do I implement this in order to satisfy both interfaces? The problem is that the event handler names are identical (OnEventReceived).
This is how I would have written it for a publisher just implementing one of the interfaces:
public interface IEventPublisher<T>
{
public delegate void EventHandler(T report);
public event EventHandler OnEventReceived;
}
public class Subscriber
{
public Subscriber(IEventPublisher<int> intPublisher)
{
intPublisher.OnEventReceived += OnIntEventReceived;
}
private void OnIntEventReceived(int report)
{
}
}
public class Publisher : IEventPublisher<int>
{
public event IEventPublisher<int>.EventHandler? OnEventReceived;
public Publisher()
{
Subscriber subscriber = new Subscriber(this);
}
private void GenerateEvents()
{
while (true)
{
...
OnEventReceived?.Invoke(number);
}
}
}
Did you try it? My IDE (with ReSharper) gives this implementation:
public class Publisher : IEventPublisher<int>, IEventPublisher<string>
{
event IEventPublisher<int>.EventHandler? IEventPublisher<int>.OnEventReceived
{
add
{
// Your code here.
}
remove
{
// Your code here.
}
}
event IEventPublisher<string>.EventHandler? IEventPublisher<string>.OnEventReceived
{
add
{
// Your code here.
}
remove
{
// Your code here.
}
}
}
And the Subscriber class would subscribe like this.
public class Subscriber
{
public Subscriber()
{
var publisher = new Publisher();
IEventPublisher<int> intPublisher = publisher;
IEventPublisher<string> stringPublisher = publisher;
intPublisher.OnEventReceived += OnIntEventReceived;
stringPublisher.OnEventReceived += OnStringEventReceived;
}
private void OnIntEventReceived(int report)
{
}
private void OnStringEventReceived(string report)
{
}
}
Note: I haven't actually tried compiling/running this.
I'm trying to do something like this but I'm getting an error along the lines of:
Cannot convert from AbstractExampleClass to SpecificExampleClassA.
So I'm probably going in the wrong direction with this code, can anyone tell me what's wrong and how to fix it.
public abstract class AbstractExampleClass
{
// Code goes here
}
public class SpecificExampleClassA : AbstractExampleClass
{
}
public class SpecificExampleClass : AbstractExampleClass
{
}
public class handler
{
public void Handle(AbstractExampleClass aec)
{
HandleSpecific(aec);
}
public void HandleSpecific(SpecificExampleClassA a)
{
// DoSomething
}
public void HandleSpecific(SpecificExampleClassB b)
{
// DoSomething Else
}
}
Overloading is resolved at compile time, so you can't expect it to decide which overload to call depending on the runtime type of aec. The overload to call must be decided at compile time.
What you really need here is the "subtyping" kind of polymorphism (aka just "polymorphism" in C# terminology), not ad hoc polymorphism (aka "overloading").
Move the handler methods in the subclasses instead:
public abstract class AbstractExampleClass
{
public abstract void Specific();
}
public class SpecificExampleClassA : AbstractExampleClass
{
public override void Specific()
{
// DoSomething
}
}
public class SpecificExampleClass : AbstractExampleClass
{
public override void Specific()
{
// DoSomething Else
}
}
public class handler
{
public void Handle(AbstractExampleClass aec)
{
aec.Specific();
}
}
That can be handled by the Visitor Pattern
public abstract class AbstractExampleClass
{
public abstract void Accept( Handler handler );
}
public class SpecificExampleClassA : AbstractExampleClass
{
public override Accept( Handler handler )
{
handler.HandleSpecific( this );
}
}
public class SpecificExampleClass : AbstractExampleClass
{
public override Accept( Handler handler )
{
handler.HandleSpecific( this );
}
}
public class Handler // the Visitor
{
public void Handle(AbstractExampleClass aec)
{
aec.Accept( this );
}
public void HandleSpecific(SpecificExampleClassA a) // visit method
{
// DoSomething
}
public void HandleSpecific(SpecificExampleClassB b) // visit method
{
// DoSomething Else
}
}
If you really have to do it that way, you can do:
public void Handle(AbstractExampleClass aec)
{
switch (aec)
{
case SpecificExampleClassA a:
HandleSpecific(a);
break;
case SpecificExampleClassB b:
HandleSpecific(b);
break;
default:
throw new Exception($"What do I do with a '{aec?.GetType()}'?");
}
}
But I recommend an approach like the one in Sweeper's answer if you are allowed to do that.
consider the following game code:
public class Player : MonoBehaviour {
public void UseItem(Item item) {
item.Use(this);
}
public void GetDrunk() {}
}
public class Item {
public WhatInterface[] itemUsages;
public void Use(Player player) {
foreach(var usage in itemUsages) {
usage.Execute(new ItemUsageArgs {itemUser = player, itemUsed = this})
}
}
}
public class GameManager : MonoBehaviour {
public Player mainCharacter;
public Item beer = new Item {itemUsages = new [] {
new TestConsole(),
new DamageFromItem (),
new DrunkFromITem ()
}}
private void Start() {
mainCharacter.Use(beer);
}
}
public class TestConsole : WhatInterface {
public void Execute(BaseArgs args) {
Debug.Log("function call executed");
}
}
public class DamageFromItem : WhatInterface {
public void Execute(ItemUsageArgs args) {
Debug.Log(args.itemUser + " take damage from " + args.itemUsed);
}
}
public class DrunkFromITem : WhatInterface {
public void Execute(ItemUsageArgs args) {
args.itemUser.GetDrunk();
}
}
public class BaseArgs {}
public class ItemUsageArgs : BaseArgs {
public Player itemUser;
public Item itemUsed;
}
so how to create interface type code that is suited for itemUsages?
Or do I wrongly create the design for this context?
Basically I'm trying strategy pattern so that item usages could be vary for every kind of item.
Things I tried, creating IItemUsage interface:
public interface IItemUsage {
void Execute(ItemUsageArgs args);
// but then anything that needs to implement this interface must use this method, even though it only needs BaseArgs.
// TestConsole class must conform to Execute(ItemUsageArgs) signature..
}
public class TestConsole : IItemUsage {
public void Execute(BaseArgs args) {
Debug.Log("function call executed");
}
// this won't compile
}
Assuming this is all of your code, you can make IItemUsage generic, and contravairant on the generic parameter.
public interface IItemUsage<in T> where T: BaseArgs {
void Execute(T args);
}
Have TestConsole implement IItemUsage<BaseArgs> and the other two classes implement IItemUsage<ItemUsageArgs>.
Now you can put instances of all three classes into an IItemUsage<ItemUsageArgs>[]:
IItemUsage<ItemUsageArgs>[] arr = new IItemUsage<ItemUsageArgs>[] {
new TestConsole(), new DamageFromItem(), new DrunkFromITem()
};
If you want to implement interface with some method, which has input arguments, that can be different types, you must define base argument class or use interface parameter instead.
For example:
public interface IItemUsage
{
void Execute(IItemUsageArgs args);
}
public interface IItemUsageArgs
{
//place public part of all ItemUsageArgs
}
public class ItemUsageArgs1 : IItemUsageArgs
{
}
public class ItemUsageArgs2 : IItemUsageArgs
{
}
public class ItemUsage1 :IItemUsage
{
public void Execute(ItemUsageArgs1 args)
{
//do you need
}
void IItemUsage.Execute(IItemUsageArgs args)
{
Execute(args as ItemUsageArgs1);
}
}
public class ItemUsage2 : IItemUsage
{
public void Execute(ItemUsageArgs2 args)
{
//do you need
}
void IItemUsage.Execute(IItemUsageArgs args)
{
Execute(args as ItemUsageArgs2);
}
}
I have a first class that raises an event when some changes occur:
public class MyFirstClass {
public event EventHandler ChangesHappened;
public MyFirstClass() {
}
public void MyMethod() {
//Thing happened
ChangesHappened?.Invoke(this, new EventArgs());
}
}
I also have a second class that have a list of FirstClass:
public class MySecondClass {
private List<MyFirstClass> first;
public MySecondClass() {
foreach(var f in first) {
first.Changed += (s, e) => {};
}
}
}
And last I have a WPF application with an instance of SecondClass. How can I handle the Changed event (FirstClass) from WPF? Should I create an event in SecondClass and raise it inside first.Changed += (s, e) => { NewEvent(this, new EventArgs()); } and then catch this in the WPF?
The objective is get the Changed event in the WPF application.
It seems to me that this is the simplest answer:
public class MySecondClass
{
public event EventHandler ChangesHappened;
private List<MyFirstClass> first;
public MySecondClass()
{
foreach (var f in first)
{
f.ChangesHappened += (s, e) => ChangesHappened?.Invoke(s, e);
}
}
}
Another option is to use Microsoft's Reactive Framework which lets you pass events (well observables) around as first-class language citizens.
You could do this:
void Main()
{
var msc = new MySecondClass();
msc.Changes.Subscribe(ep =>
{
/* Do stuff with
ep.Sender
ep.EventArgs
from the `MyFirstClass` instances
*/
});
}
public class MyFirstClass
{
public event EventHandler ChangesHappened;
public IObservable<EventPattern<EventArgs>> Changes;
public MyFirstClass()
{
this.Changes = Observable.FromEventPattern<EventHandler, EventArgs>(
h => this.ChangesHappened += h, h => this.ChangesHappened += h);
}
public void MyMethod()
{
ChangesHappened?.Invoke(this, new EventArgs());
}
}
public class MySecondClass
{
public IObservable<EventPattern<EventArgs>> Changes;
private List<MyFirstClass> first = new List<MyFirstClass>();
public MySecondClass()
{
this.Changes = first.Select(f => f.Changes).Merge();
}
}
As #Enigmativity already mentioned: When you have a class, that has to manage other classes (bunch of MyFirstClass references) then you have to forward your events from sub class to manager class.
public class MySecondClass
{
public event EventHandler Changed;
private List<MyFirstClass> firstClassList;
public MySecondClass()
{
firstClassList = new List<MyFirstClass>();
}
public void AddMyFirstClassList(List<MyFirstClass> firstClassList)
{
foreach (var firstClass in firstClassList)
AddMyFirstClass(firstClass);
}
public void AddMyFirstClass(MyFirstClass firstClass)
{
// from sub class to manager class
firstClass.Changed += firstClass_Changed;
firstClassList.Add(firstClass);
}
private void firstClass_Changed(object sender, EventArgs args)
{
Changed?.Invoke(sender, args);
}
public void RemoveMyFirstClass(MyFirstClass firstClass)
{
MyFirstClass.Remove -= firstClass_Changed;
firstClassList.Remove(firstClass);
}
}
Another option is to pass a callback function. You should avoid this, unless you need it explicity:
public class MyFirstClass
{
EventHandler handler;
public MyFirstClass(EventHandler handler)
{
this.handler = handler;
}
public void MyMethod()
{
handler?.Invoke(this, new EventArgs());
}
}
public class MySecondClass
{
private List<MyFirstClass> firstClassList;
public MySecondClass()
{
firstClassList = new List<MyFirstClass>();
}
// you have instantiated your class and passed your callback function previously.
public void AddMyFirstClass(MyFirstClass firstClass)
{
firstClassList.Add(firstClass);
}
// for demonstrating the instantiation.
public void AddMyFirstClass(EventHandler handler)
{
firstClassList.Add(new MyFirstClass(handler));
}
}
I want to Override base class methods in derived class and then do something in derived class. therefore the base class method be invoked with its generic type . Then I aim to fire the derived class method which is overridden.
I have below codes :
public class Service<T> : Interface.IService<T> where T : class
{
public virtual event System.EventHandler<EntitySavingEventArgs<T>> BeforeSavingRecord;
public Service()
{
}
public virtual void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<T> e)
{
}
private readonly DbContext _dbContext;
public Service(DbContext dbContext)
{
_dbContext = dbContext;
}
public virtual void Create(T item)
{
if (item == null)
throw new ArgumentNullException("item");
BeforeSavingRecord?.Invoke(this, new EntitySavingEventArgs<T>() { SavedEntity = item });
_dbContext.Set(typeof(T)).Add(item);
_dbContext.SaveChanges();
}
}
and in its derrived classes I have something like this :
[Service]
public partial class BankBusiness : Service<Bank>, IBankBusiness
{
public BankBusiness()
: base(ContainerManager.Container.Resolve<MyContext>())
{
}
public override void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<Bank> e)
{
//Do something with entity item before saving
base.OnBeforeSavingRecord(sender, e);
}
}
And then in my controllers when I call
bankBiz.Create(new Bank() { ... });
I want to fire bankBiz (derrived class) overridden method (OnBeforeSavingRecord) which is registered to BeforeSavingRecord event.
I donot know if my scenario is correct and if it is right how i can fire it.
if it is not correct what I should do.
I had a similar pattern implemented in the base class I did it in a way like this:
Base:
public virtual void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<T> e)
{ }
and in the derived class I had exactly the call that you use:
Derivied:
public override void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<Bank> e)
{
//Do something with entity item before saving
base.OnBeforeSavingRecord(sender, e);
}
To fire the event in my case it was sufficient to just call
OnBeforeSavingRecord(this, new EntitySavingEventArgs<T>() { SavedEntity = item });
and the whole scenario worked for me.
EDIT:
Calling OnBeforeSavingRecord will executed the code in the overriden method of the derived class if you call it from an instance of the derived class!
If I understand you right you want to fire the event in the method Create but want the event code of the derived class executed. It will be if the event is overridden like in your case. You could test it with this simple Console application: (just copy paste and run)
public class Service
{
public virtual event System.EventHandler<EventArgs> BeforeSavingRecord;
public virtual void OnBeforeSavingRecord(object sender, EventArgs e)
{
Console.WriteLine("Base: OnBeforeSavingRecord method call");
}
public virtual void Create(object item)
{
Console.WriteLine("Base: Create method call");
// this will call the method of the derived class! if you call it from an instance of the derived class
OnBeforeSavingRecord(this, new EventArgs());
}
}
public partial class BankBusiness : Service
{
public override void OnBeforeSavingRecord(object sender, EventArgs e)
{
//Do something with entity item before saving
Console.WriteLine("Derived Class OnBeforeSavingRecord CALL");
base.OnBeforeSavingRecord(sender, e);
}
}
static void Main(string[] args)
{
BankBusiness bankBiz = new BankBusiness();
bankBiz.Create(new object());
Console.ReadKey();
}
#Mong Zhu your solution works but not for my scenario . I came up with the below solution
public class Service<T> : Interface.IService<T> where T : class
{
Interface.IService<T> implementation;
public virtual event System.EventHandler<EntitySavingEventArgs<T>> BeforeSavingRecord;
public virtual event System.EventHandler<EntitySavingEventArgs<T>> SavingRecord;
public virtual event System.EventHandler<EntitySavingEventArgs<T>> RecordSaved;
public void PopulateEvents(Interface.IService<T> _implementation)
{
implementation = _implementation;
implementation.BeforeSavingRecord += new System.EventHandler<EntitySavingEventArgs<T>>(this.OnBeforeSavingRecord);
implementation.SavingRecord += new System.EventHandler<EntitySavingEventArgs<T>>(this.OnSavingRecord);
implementation.RecordSaved += new System.EventHandler<EntitySavingEventArgs<T>>(this.OnRecordSaved);
}
public virtual void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<T> e)
{
}
public virtual void OnSavingRecord(object sender, EntitySavingEventArgs<T> e)
{
}
public virtual void OnRecordSaved(object sender, EntitySavingEventArgs<T> e)
{
}
private readonly DbContext _dbContext;
public Service(DbContext dbContext)
{
_dbContext = dbContext;
}
public virtual void Create(T item)
{
if (item == null)
throw new ArgumentNullException("item");
BeforeSavingRecord?.Invoke(this, new EntitySavingEventArgs<T>() { SavedEntity = item });
_dbContext.Set(typeof(T)).Add(item);
SavingRecord?.Invoke(this, new EntitySavingEventArgs<T>() { SavedEntity = item });
_dbContext.SaveChanges();
RecordSaved?.Invoke(this, new EntitySavingEventArgs<T>() { SavedEntity = item });
}
}
and the derived class :
[Service]
public partial class BankBusiness : Service<Bank>, IBankBusiness
{
public BankBusiness()
: base(ContainerManager.Container.Resolve<MyContext>())
{
base.PopulateEvents(this);
}
public override void OnBeforeSavingRecord(object sender, EntitySavingEventArgs<Bank> e)
{
base.OnBeforeSavingRecord(sender, e);
}
}
which the point is
base.PopulateEvents(this);