I've got a class with a list of properties, and the properties themselves have a list of properties. I need the "grandchild"- property to be able to request data from it's parent's parent (sorry for the confusing terms - there's no inheritance here). To clarify:
class ClassA
{
list<ClassB> Children
var SomeOtherProperty
}
class ClassB
{
list<ClassC> Grandchildren
}
class ClassC
{
var GetSomeOtherProperty()
{
...
}
}
The data may change during run time so I can't just pass it once and be done with it, I gotta be able to get it dynamically.
I could always pass the parent all the way down to the grandchild in the ctors, but I was taught it's a bad practice, so I'd rather avoid it.
I've been reading about passing data back/downwards via events using mutable EventArguments- I'm wondering if that'd be an OK solution for what I've got (I'll need to do it twice each time- sort of chase the tail of the first eventarg). Are there any pitfalls I need to be aware of?
Do I have any other options for this situation?
Thanks!
If possible, you can use the design pattern composite. First, create a base class 'note' with a list of notes for the children and a virtual method 'DoAction'. Then derive all other classes from this class and override the method with own implementation.
Now you can build up a tree of notes and do a traversal on all of it childs. For each of them, call 'DoAction'.
Hope you get the idea...
Since classes A, B & C are not liked via inheritance, therefore, I would like to avoid any direct wiring between these classes as it will make the design a little complicated as the system would evolve.
Moreover, since class B has nothing to do with the data, I dont want it to get effected by it.
Therefore, I would like to take out the communicate via a separate route to keep the classes loosely coupled -
here is a sample code to explain my idea -
public interface IPublisher
{
event EventHandler OperationOccurred;
}
class ClassA : IPublisher
{
List<ClassB> Children;
event EventHandler OperationOccurred;
public ClassA()
{
BroadCaster.Instance.RegisterPublisher(this);
}
protected virtual void OnOperationOccurred()
{
if (OperationOccurred != null)
OperationOccurred(this, new EventArgs());
}
}
class ClassB
{
List<ClassC> Grandchildren;
}
class ClassC
{
public ClassC()
{
BroadCaster.Instance.BroadCastNotificaiton += Instance_OperationOccurred;
}
void Instance_OperationOccurred(object sender, EventArgs e)
{
throw new NotImplementedException();
}
}
/// <summary>
/// A singleton class ... Like a single braodcast tower just one in the city
/// </summary>
public sealed class BroadCaster
{
public static BroadCaster Instance { get; private set; }
//Static constructor
static BroadCaster()
{
Instance = new BroadCaster();
}
// private constructor
private BroadCaster(){}
public event EventHandler BroadCastNotificaiton;
public void RegisterPublisher(IPublisher publisher)
{
publisher.OperationOccurred += Publisher_OperationOccurred;
}
void Publisher_OperationOccurred(object sender, EventArgs e)
{
if (this.BroadCastNotificaiton != null)
this.BroadCastNotificaiton(sender, e);
}
}
I have implemented broadcaster a singleton object, but all I want to convey is a single point of communication.
Hope it helps.
Related
This problem has been keeping me busy for half a day now and I start to lose my sanity:
I'm using Items for UI Logic stuff. There are "parent" Items, that can contain ObservableCollections of other Items. (Both inherit from the same ItemBase, picture nodes with nodes, sort of recursive)
For not having to recreate Observer logic on each "parent" item class, I wanted to add the functionality to the common baseclass, called ItemBase. The idea is, that the parent can just register its ObservableCollections and the baseclass takes care of the event routing and all. The problem is, that I can't seem to find a way to save a reference to these ObservableCollections (of different types with the same baseclass) for the way that generics work.
Here's the code:
public abstract class ItemBase : ViewModelBase
{
private List<ObservableItemCollection<ItemBase>> _trackedChildItemsList = new List<ObservableItemCollection<ItemBase>>();
public event EventHandler<ItemPropertyChangedEventArgs> ChildItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ChildItemIsDirtyChanged;
public override bool IsDirty
{
get { return base.IsDirty || AreAnyChildItemsDirty; }
set { base.IsDirty = value; }
}
private bool AreAnyChildItemsDirty
{
get
{
return _trackedChildItemsList.Any(i => i.Any(l => l.IsDirty));
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection); // intellisense underlines 'collection'; cannot convert from 'ObservableItemCollection<T>' to ObservableItemCollection<ItemBase>:
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (ObservableItemCollection<ItemBase> collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
OnChildItemPropertyChanged(e);
}
protected virtual void OnChildItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ChildItemPropertyChanged?.Invoke(this, e);
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
OnItemIsDirtyChanged(e);
}
protected virtual void OnItemIsDirtyChanged(IsDirtyChangedEventArgs e)
{
ChildItemIsDirtyChanged?.Invoke(this, e);
}
}
As you can see, I'm using a derived, custom type of the ObservableCollection, namely ObservableItemCollection, which takes care of the ItemPropertyChanged and ItemIsDirtyChanged invokation for the collection itself. This allows one to catch those events from the outside.
Now, instead of having that 'catching the events' logic in each parent item itself (duplicated), I wanted it to be in a centralized spot, namely the baseclass.
Now the main problem is, that upon registering the ObservableItemCollections, I cannot possibly keep a reference to them since there's no common base. ObservableItemCollection<CustomItem> does not inherit from ObservableItemCollection<ItemBase>, since its a collection. I tried solving the whole thing with generics, however, the above is as far as I got. It fails to compile where i wrote the 'cannot convert from 'ObservableItemCollection' to ObservableItemCollection' comment.
I understand why it fails to compile, however, I can't seem to find a workaround/working solution.
I absolutely need a direct reference to the collections (casted as my custom type ObservableItemCollection), else the whole thingy won't work. You can see in the code that I'm accessing both the events of the collection itself, as well as properties of the ItemBase.
Either way, I can't seem to find a common base for the collections. I tried using dynamics and reflection based casting, Interfaces, a Custom generic ParentItem type, neither worked (i might have overlooked something) and even if it did, it would be rather ugly.
Is it really not possible to achieve what I want with a limited amount of hacking things together? I can't believe that I didn't find a good solution after all the time I've invested in this.
Additional info:
In the parent item i have the following ObservableCollections:
public ObservableItemCollection<SomeItem1> Collection1 { get; set; } = new ObservableItemCollection<SomeItem1>();
public ObservableItemCollection<SomeItem2> Collection2 { get; set; } = new ObservableItemCollection<SomeItem2>();
Where both item types inherit from ItemBase. Then i call the base method RegisterItemCollection in the parent item constructor like so:
RegisterItemCollection(Collection1);
RegisterItemCollection(Collection2);
WPF collection controls have the same problem: How do you define a property which can hold a reference to any kind of generic collection? Answer: Make the property a reference to a non-generic interface that all the collections implement. This is a very general question, and it's the reason why non-generic System.Collections.IEnumerable and System.Collections.IList are still in heavy use throughout the .NET framework, all these years after generics were introduced.
Nothing you're doing in RegisterItemCollection(), IsDirty, or Dispose() needs to care about the type of item in the collection. So take whatever methods and properties you need that code to interact with, and put it all in a non-generic interface or base class. Your base class is already generic (ObservableCollection<T>, I presume), so use an interface.
public interface IObservableItemCollection
{
event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
bool IsDirty { get; }
}
public interface IDirtyable
{
// I'm pretty sure you'll want this event here, and I think you'll want your collection to
// implement IDirtyable too.
//event EventHandler<IsDirtyChangedEventArgs> IsDirtyChanged;
bool IsDirty { get; }
}
public class ObservableItemCollection<T>
: ObservableCollection<T>, IObservableItemCollection
where T : IDirtyable
{
public bool IsDirty => this.Any(item => item.IsDirty);
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
}
public class ViewModelBase : IDisposable, IDirtyable
{
public virtual bool IsDirty => true;
public virtual void Dispose()
{
}
}
public class ItemBase : ViewModelBase
{
private List<IObservableItemCollection> _trackedChildItemsList = new List<IObservableItemCollection>();
public override bool IsDirty
{
get
{
return base.IsDirty || _trackedChildItemsList.Any(coll => coll.IsDirty);
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection);
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (IObservableItemCollection collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
}
}
public class ItemPropertyChangedEventArgs : EventArgs
{
}
public class IsDirtyChangedEventArgs : EventArgs
{
}
You could also do this by making _trackedChildItemsList a collection of IDisposable, and have the collections clear their own event handlers, but a class clearing its own event handlers is pretty gruesome. Shun reflection when conventional OOP can be used to do the job in a readable and maintainable way. And you'd still have to think of something for IsDirty.
You can not do this since if you could you could do something like
class A {}
class B : A { }
class C : A { }
var list = new List<List<A>>();
var sublist_b = new List<B>();
sublist_b.Add(new B());
list.Add(sublist_b);
var sublist = list.Single();
sublist.Add(new C()); // <- now a List<B> contains an object that ist not if type B or derived B
I would suggest that you only use ObservableItemCollection<ItemBase> to hold your objects.
How,does one should call an event declared by interface so that all the classes that has implemented that interface get notified??
For example in structure like this,
public delegate void myDel(int value);
interface IEventCaller{
event myDel myDelEventCall;
}
public Class One : IEventCaller {
public event myDel myDelEventCall;
}
public Class Two : IEventCaller {
public event myDel myDelEventCall;
}
I want both class One and Two to get notify and act as event gets called, I am feeling somewhere I am going wrong direction , is it possible to do?
Actually what you want doesn't involve events. Events would be used by an object implementing IEventCaller to notify some object holding a reference to that object of some change. To invoke something on the object implementing IEventCaller would just require a method, for example Hello();
First, you need code that informs all the objects that implement this interface. To make that possible, you somewhere need to store a list of instances that want to get notified.
One solution would be to create a class that manages that list. Let's say like this
private static List<IEventCaller> eventCallers = new List<IEventCaller>();
public static void AddEventCaller(IEventCaller c)
{
eventCallers.Add(c);
}
public static void RemoveEventCaller(IEventCaller c)
{
eventCallers.Remove(c);
}
public static IEventCaller[] EventCallers
{
get { return eventCallers.ToArray() }
}
Of course this code needs to be thread safe, etc. I'd put all this into a singleton to be globally available.
Then, all objects that implement IEventCallers need to register/unregister accordingly. Thus, I'd also have them Implement IDisposable so that in the constructor you can do
public EventCallable()
{
Singleton.Instance.AddEventCaller(this);
}
and in the Dispose method you can do this:
public void Dispose(bool disposing)
{
Singleton.Instance.RemoveEventCaller(this);
}
Now the code that should notify every instance could just do this:
public void NotifyAll()
{
foreach (IEventCaller caller in Singleton.Instance.EventCallers)
caller.Hello();
}
I think you might be looking at this the other one around.
With events, you want to have an object which is the publisher, which is responsible for publishing the event and saying "hey guys, something just occurred and you should know about it", and you have your subscribers, which are the guys who say "Yo dawg, let me know when that thing occurs, so i can act on it".
What you can do is have the object which is responsible for the event occurring implement your interface:
public class Publisher : IEventCaller
{
public event MyDel MyDeleteEvent;
public void OnDeleteOccured()
{
var myDeleteEvent = MyDeleteEvent;
if (myDeleteEvent != null)
{
MyDeleteEvent(1);
}
}
}
And then have your One and Two objects register to that event occurring, where they pass a method which signature matches the delegate type of MyDel:
public class SubscriberOne
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
public class SubscriberTwo
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
And the registration goes:
void Main()
{
var publisher = new Publisher();
var subscriberOne = new SubscriberOne();
var subscriberTwo = new SubscriberTwo();
publisher.MyDeleteEvent += subscriberOne.OnSomethingOccured;
publisher.MyDeleteEvent += subscriberTwo.OnSomethingOccured;
}
Quite some time ago, I noticed that the Windows Forms editor of Visual Studio does not support events which contain generic type parameters. For example, an event like
public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } }
where
public class ListEventArgs<T> : EventArgs { List<T> args; }
does not even show up in the event list in the property manager of Visual Studio. Now, this is a somewhat artificial example that could easily be modified to work in Visual Studio by rewriting the classes and their events. However, I am currently working on a project where I cannot change some classes for compatibility reasons. The only thing I can do is to change the events of my user control. The events of this control currently look like this:
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
Note that the underlying Plane class (represented by the _Plane instance which is a protected field) cannot be changed. Its DrawingError event and its EventArgs type are declared in the Plane class like this:
public class Plane<T> where T : ISurface
{
...
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
...
public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
Of course, the Windows Forms editor of Visual Studio does not show any of the events of my user control. I have been looking for a number of workarounds to get them shown again, but have not been able to find a workaround that actually works. Here are some things that I tried:
Created a MyPlane class which inherits from Plane and used that instead: public event EventHandler<MyPlane.DrawingErrorEventArgs> DrawingError .... For reasons unknown to me, the events still don't show up in the editor. Perhaps this is due to the parameters of the event, some of which still are generic. Find a minimal working example below.
Created a helper class which defines implicit conversion operators between EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> and EventHandler<GDIPlane.DrawingErrorEventArgs> where GDIPlane is just a dummy class which inherits from Plane<GDISurface>. This does work to some extent, but duplicates event calls since the conversion creates new event handlers which are passed down to _Plane which cannot be removed/unregistered properly.
Tried to inherit from EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>, which obviously does not work since EventHandler<T> is sealed.
Are there any other ways to make my events visible again in the Windows Forms editor?
Best regards
Andreas
EDIT: Minimal working example for 1:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
public class GDIPlane : Plane<GDISurface> { }
GDIPlane _Plane = null;
public event EventHandler<GDIPlane.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
DrawingError does not show up in the list of events in the property manager when clicking on a TestControl instance.
EDIT2: This is the original problem (without any workarounds) where the DrawingError event does of TestControl does not show up either:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
Plane<GDISurface> _Plane = null;
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
This is behavior specific to Visual Studio, and the cause is rooted in the fact that EventHandler<> does not specify covariance on its 'TEventArgs' (it would impose seemingly silly restrictions) and the tools do not perform enough introspection of your code to suss out an appropriate type (even though you've left a trail of type data in constructing the control.) Thus, it seems as though VS does not support generic event properties. You may consider filing a feature request on Microsoft Connect, I wouldn't suggest filing it as a bug as they may label it "by design" and close it.
As a general rule, if you need generic type parameters on your events and you need design time support for them (which are different implementation concerns), you're looking at wrapping them in a presentation-specific facade (e.g. "extra layer of code to facilitate design-time needs".)
Personally, I would reduce the generic typing you have in play now, it seems a bit excessive and if you don't understand covariance/contravariance in generic types it might put you in a tight spot at some point, such as now.
However, to work around your problem:
Consider using a custom event args class which could transport data in a non-generic property, and also use a non-generic EventHandler event/property. Understanding the 'type' of the event is then shifted away from generic type parameters and made the responsibility of your non-generic event args instead. If the 'class' of the event args is insufficient, you can add a property to convey the event type (or data type) so that receiving code can properly interpret it (assuming, of course, that it does not already know by some other means.):
public class DataEventArgs : EventArgs
{
//public string EventTypeOrPurpose { get; set; }
public object Data { get; set; }
}
This is most often only used to ferry data through an event chain, and it is usually implemented as follows:
public class DataEventArgs<T> : EventArgs
{
public T Data { get; set; }
}
Unfortunately, this also has a covariance problem, to resolve it you would actually want something more like this:
public interface IDataArgs<out T>
{
T Data { get; }
}
public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
public DataEventArgs<T>(T data)
{
_data = data;
}
private T _data;
public T Data { get { return _data; } }
}
Even so, these generic versions still don't work around Visual Studio's limitations, this is merely more proper alternative forms of what you already have shown us.
UPDATE: As requested, here is what a "purpose built facade" might look like in the most basic sense. Note that the usercontrol functions as a facade layer in this case as the eventhandler it exposes delegates to the underlying object model. There is no direct access to underlying object model from the user control (from consumer/designer perspective.)
Please note the reference tracking for event handlers is not necessary unless you dispose of these user controls throughout the lifetime of the app (it is only done to ensure proper delegate removal based on the delegate provided, which is wrapped in a closure/delegate, as you see below.)
Also worth noting I did not test-run this code beyond verifying that the designer shows DrawingError in the property grid when dropped onto a form.
namespace SampleCase3
{
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
private Plane<GDISurface> _Plane = new Plane<GDISurface>(); // requires initialization for my own testing
public TestControl()
{
}
// i am adding this map *only* so that the removal of an event handler can be done properly
private Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>> _cleanupMap = new Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>>();
public event EventHandler DrawingError
{
add
{
var nonGenericHandler = value;
var genericHandler = (EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>)delegate(object sender, Plane<GDISurface>.DrawingErrorEventArgs e)
{
nonGenericHandler(sender, e);
};
_Plane.DrawingError += genericHandler;
_cleanupMap[nonGenericHandler] = genericHandler;
}
remove
{
var nonGenericHandler = value;
var genericHandler = default(EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>);
if (_cleanupMap.TryGetValue(nonGenericHandler, out genericHandler))
{
_Plane.DrawingError -= genericHandler;
_cleanupMap.Remove(nonGenericHandler);
}
}
}
}
}
To complement the above, here is what a non-generic event handler would now look like:
private void testControl1_DrawingError(object sender, EventArgs e)
{
var genericDrawingErrorEventArgs = e as Plane<GDISurface>.DrawingErrorEventArgs;
if (genericDrawingErrorEventArgs != null)
{
// TODO:
}
}
Note that the consumer here has to have knowledge of the type for e to perform conversion. The use of the as operator will bypass ancestry checks under the assumption that the conversion should succeed.
Something like this is as close as you're going to get. Yes it is ugly by most of our standards, but if you absolutely 'need' design-time support on top of these components and you cannot change Plane<T> (which would be more appropriate) then this, or something close to this, is the only viable workaround.
HTH
I am working on an LOB application in C# using a WinForms tabbed MDI interface. I have various forms with DataGridViews to allow the user to select an object they are interested in, which they can then view/edit in a new form.
Each of my main business objects inherit from Entity, which is defined as below:
public abstract class Entity
{
public static event Action Saved;
internal virtual void OnSaved()
{
if (Saved != null)
{
Saved();
}
}
}
I then have the objects that populate the grid (these are actually auto-generated classes from Linq-to-SQL, although I can replicate the problem with normal classes):
class Class1 : Entity
{
//Stuff
}
class Class2 : Entity
{
//Stuff
}
I want to know when an object of a given class is modified, but i don't care which instance (hence the static action) so that i can refresh the grid and perform other activities.
The problem comes when the event is fired from a derived class instance - it fires for all other derived classes too. For example:
Class1.Saved += new Action(s1);
Class2.Saved += new Action(s2);
private void TestIt()
{
Class2 o2 = new Class2();
o2.OnSaved();
}
This would fire s1 and s2, but I only want the specific one to be fired (i.e. s2). What is the best way to do this? I have quite a few classes that need this behviour and would like to avoid having to add any code to each class if possible.
Update:
Thank you for all your responses, they have been very helpful.
I have opted for a slightly different option, which I admit seems quite hacky, but works well for my purposes. This involves passing the type with the action and letting a handler filter and call relevant operations.
Entity Class:
public abstract class Entity
{
public static event Action<Type> Saved;
internal void OnSaved()
{
private Action<Type> SavedCopy = Saved;
if (SavedCopy != null)
SavedCopy(this.GetType());
}
}
Hook up handler:
Entity.Saved += new Action<Type>(Handler);
Example Handler method (this will vary from form to form):
void Handler(Type obj)
{
if (obj==typeof(Class1))
UpdateGrid();
else if (obj==typeof(Class2))
UpdateBasicInfo();
else if (obj == typeof(Class3))
DoAnotherThing();
}
Using generics could be a work around; each generic class gets a copy of the static fields.
public abstract class Entity<T>
{
public static event Action Saved = delegate { };
internal virtual void OnSaved()
{
Saved();
}
}
class Class1 : Entity<Class1>
{
//Stuff
}
class Class2 : Entity<Class2>
{
//Stuff
}
I'm not sure doing it like this is a good idea, but you could specify the type when you subscribe and when you save the data:
public abstract class Entity
{
private static Dictionary<Type, Action> Subscribers
= new Dictionary<Type, Action>();
internal virtual void OnSaved()
{
OnSaved(GetType());
}
private OnSaved(Type type)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
if (subscribed != null)
subscribed();
}
public Subscribe(Type type, Action action)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
Subscribers[type] = subscribed + action;
}
public Unsubscribe(Type type, Action action)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
Subscribers[type] = subscribed - action;
}
}
Keep in mind that this code is not thread-safe, so if you want to use it from different threads at the same time, you need to add locking.
You will need to have an event per type, because can't determine for which type the delegate is registered when the event is defined on the base type.
public abstract class Entity
{
internal abstract void OnSaved();
}
class Class1 : Entity
{
public static event Action Saved = () => { };
internal override void OnSaved()
{
this.Saved();
}
//Stuff
}
class Class2 : Entity
{
public static event Action Saved = () => { };
internal override void OnSaved()
{
this.Saved();
}
//Stuff
}
Why does it have to be static? Make it an instance event instead.
public event Action Saved;
You have to hook it up for each instance instead of just once per class (or, in your current case, once), but it will separate the events.
I kinda know what polymorphism is but failed to understand it clearly. Also my code is following:
class Human
{
public virtual void CleanTheRoom()
{
}
}
class Woman:Human
{
public override void CleanTheRoom()
{
//women clean faster
}
}
class Man:Human
{
public override void CleanTheRoom()
{
//men clean slower, different code here
}
}
class Child:Human
{
public override void CleanTheRoom()
{
//empty ... children are lazy :)
}
}
Should I explain this is polymorhism because all derived classes from base class Human contain method CleanTheRoom but each of them it implements differently?
The benefit of polymorphism comes when you want to invoke the method on some type of Human, but you don't care which one specifically.
By having CleanTheRoom() defined at the base class level, Human, you can write shorter, cleaner code elsewhere in your application whenever you are working with an instance of Human, whether it be a Child or otherwise.
Polymorphism, for example, lets you avoid lengthy conditional statements where you explicitly check for each type of Human and call a different method:
Good:
private void SomeMethod(Human h)
{
//some logic
h.CleanTheRoom();
//more logic
}
Bad:
private void SomeMethod(Human h)
{
//some logic
if (h is Adult)
CleanTheRoom();
else if (h is Child)
GoofOff();
//some logic
}
What you have is a good example of inheritance. Polymorphism refers specifically to being able to refer to objects of different types by using a single type (the parent class or interface), something this type of inheritance makes possible. Like so:
List<Human> humans = new ArrayList<Human>();
humans.add(new Woman());
humans.add(new Woman());
humans.add(new Man());
humans.add(new Child());
humans.add(new Child());
foreach(Human hum in humans) {
hum.CleanTheRoom(); //I don't know the type of hum, but I don't care
}
Say I've been collecting instances of Human from various locations -- I don't know what type each one is. But I can still iterate over them and call CleanTheRoom(), because they share a parent class.
I'll add a real-world example. Say I have an Invoice class with various subclasses for different types of Invoices -- maybe there are different kinds of Invoices for service clients versus customers who make one-time purchases. Sometimes I care deeply about the differences, and I only deal with one type. But sometimes I want to loop through all of the invoices for this month and print them out. If the parent class has a print() method (which may well be implemented differently by different types) then I can do that.
Yes, that is correct. And you can call the method CleanTheRoom() without knowing which "kind" of human is it.
Here you have some basic examples.
I think you fail to see the benefit, that's the key you're missing to fully understand polymorphism. I will try to make an example:
Let's say you have a simple CRUD form. This is the code of the save button:
var Client = PopulateDTO(); //put all the values in the controls, to an object
if(Action==Actions.Create){
_repository.Create(Client);
}
else if(Action==Actions.Update){
_repository.Update(Client);
}
else if(Action==Actions.Delete){
_repository.Delete(Client);
}
this.Close();
This code works, but it's bad code, and difficult to read. Let's use polymorphism (and the strategy pattern):
public abstract class BaseStrategy{
abstract void Do(ClientDto Client);
}
public class CreateStrategy:BaseStrategy{
public override void Do(ClientDto Client){
_repo.Save(Client);
}
}
public class UpdateStrategy:BaseStrategy{
public override void Do(ClientDto Client){
_repo.Update(Client);
}
}
public class DeleteStrategy:BaseStrategy{
public override void Do(ClientDto Client){
_repo.Delete(Client);
}
}
So, we have an abstract class, and 3 implementations, each one doing something with the client object. Now, the code of the save button in the form will be:
BaseStrategy stg = GetCorrectStrategy();
var Client = PopulateDTO();
stg.Do(Client);
this.close;
And the method GetCorrectStrategy() will instantiate the correct Strategy implementation, depending if the user is creating, editing or deleting the client.
I hope this answer will help you. But if didn't help you, I suggest you read about strategy pattern, It's one of the best uses of polymorphism in my opinion
Since several people have already given fine examples of polymorphism, I'll offer a different perspective that really helped me to grok it.
In functional programming, functions are the first class concepts in contrast to OOP where objects are supreme.
Polymorphism is to OOP what pattern matching is to FP. Here is a function that uses pattern matching (using an ML style syntax).
let f x =
match x with
| T -> //do stuff with a T to return some value
| S -> //do stuff with an S to return some value
| U -> //do stuff with a U to return some value
| V -> //do stuff with a V to return some value
So when you use the function f, you can pass it an object of either type T, S, U, or V. In strongly typed FP languages like F#, the type of x is denoted T|S|U|V. Such types are commonly referred to as Sum types or Tagged Unions.
If we fix up your example to make Human an abstract class, then it will become clear that polymorphism in OOP just gives you a way of expressing a sum type.
Thus, CleanTheRoom is a function that takes a type Human. But Human is just the name for the type Man|Woman|Child which is a sum type. The big difference between languages like C# and functional languages like F# is that one treats objects as top level things while the other treats functions as top level things. Also, everything in OOP languages like C# must have names. In a functional language we could denote the type Man|Woman|Child without having to explicitly name it.
The key is not to think of the code as having different CleanTheRoom methods, but rather think of CleanTheRoom as one method that takes a type Man|Woman|Child (which is named Human). Polymorphism is just the implementation detail.
In summary, polymorphism (especially with abstract classes) basically just give you a way to name sum types and do pattern matching.
See:
http://en.wikipedia.org/wiki/Tagged_union
http://en.wikipedia.org/wiki/Algebraic_data_type
An example in C#:
This is my class file
class parent
{
public virtual string saySomething(string s)
{
return s+":Parent";
}
}
class man : parent
{
public override string saySomething(string s)
{
return s+":Man";
}
}
class woman : parent
{
public override string saySomething(string s)
{
return s+":Woman";
}
}
class child : parent
{
public override string saySomething(string s)
{
return s+":Child";
}
}
Create Four Buttons and a label.
Here is the implementation on a simple form1
private void Form1_Load(object sender, EventArgs e)
{
p1= new parent();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = p1.saySomething("I am parent!");
}
private void button2_Click(object sender, EventArgs e)
{
p1 = new man();
label1.Text = p1.saySomething("I am man!");
}
private void button3_Click(object sender, EventArgs e)
{
p1 = new woman();
label1.Text = p1.saySomething("I am woman!");
}
private void button4_Click(object sender, EventArgs e)
{
p1 = new child();
label1.Text = p1.saySomething("I am child!");
}
Is it run-time polymorphism?
P1 is an object. Depending upon the situation (Context), a button click, it is executing different piece of code. So, p1 is behaving differently depending upon the click event.
class Program
{
static void Main(string[] args)
{
List<ICleanTheRoom> cleanerList = new List<ICleanTheRoom>
{
new Child(),
new Woman(),
new Man()
};
foreach (var cleaner in cleanerList)
{
cleaner.CleanTheRoom();
}
}
}
internal interface ICleanTheRoom
{
void CleanTheRoom();
}
// No need for super type
//class Human : ICleanTheRoom
//{
// public virtual void CleanTheRoom()
// {
// }
//}
internal class Woman : ICleanTheRoom
{
public void CleanTheRoom()
{
throw new NotImplementedException();
}
}
class Man: ICleanTheRoom
{
public void CleanTheRoom()
{
throw new NotImplementedException();
}
}
class Child: ICleanTheRoom
{
public void CleanTheRoom()
{
throw new NotImplementedException();
}
}
Is it a new object created each time at runtime, clearly inheriting but no polymorphing.