I can't seem to find out the answer to this through searching, so here goes....
I know that I can pass Class objects generically to other classes by utilising this type of code:
public class ClsGeneric<TObject> where TObject : class
{
public TObject GenericType { get; set; }
}
Then constructing in this way:
ClsGeneric<MyType> someName = new ClsGeneric<MyType>()
However, I have an application that requires me to open a Form and somehow pass in the generic type for use in that form. I am trying to be able to re-use this form for many different Class types.
Does anyone know if that's possible and if so how?
I've experimented a bit with the Form constructor, but to no avail.
Many thanks in advance,
Dave
UPDATED: A Clarification on what the outcome I am trying to achieve is
UPDATED: 4th AUG, I've moved on a little further, but I offer a bounty for the solution. Here is what I have now:
interface IFormInterface
{
DialogResult ShowDialog();
}
public class FormInterface<TObject> : SubForm, IFormInterface where TObject : class
{ }
public partial class Form1 : Form
{
private FormController<Parent> _formController;
public Form1()
{
InitializeComponent();
_formController = new FormController<Parent>(this.btnShowSubForm, new DataController<Parent>(new MeContext()));
}
}
public class FormController<TObject> where TObject : class
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
showSubForm("Something");
}
public void showSubForm(string className)
{
//I'm still stuck here because I have to tell the interface the Name of the Class "Child", I want to pass <TObject> here.
// Want to pass in the true Class name to FormController from the MainForm only, and from then on, it's generic.
IFormInterface f2 = new FormInterface<Child>();
f2.ShowDialog();
}
}
class MeContext : DbContext
{
public MeContext() : base(#"data source=HAZEL-PC\HAZEL_SQL;initial catalog=MCL;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework") { }
public DbSet<Parent> Child { get; set; }
}
public class DataController<TObject> where TObject : class
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class Parent
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child
{
string Name { get; set; }
int Age { get; set; }
}
Maybe you've tried this, but you can create a custom class:
public class GenericForm<TObject> : Form where TObject : class
{
// Here you can do whatever you want,
// exactly like the example code in the
// first lines of your question
public TObject GenericType { get; set; }
public GenericForm()
{
// To show that this actually works,
// I'll handle the Paint event, because
// it is executed AFTER the window is shown.
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
// Let's print the type of TObject to see if it worked:
MessageBox.Show(typeof(TObject).ToString());
}
}
If you create an instance of it like that:
var form = new GenericForm<string>();
form.Show();
The result is:
Going further, you can create an instance of type TObject from within the GenericForm class, using the Activator class:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
In this example, since we know that is a string, we also know that it should throw an exception because string does not have a parameterless constructor. So, let's use the char array (char[]) constructor instead:
GenericType = (TObject)Activator.
CreateInstance(typeof(TObject), new char[] { 'T', 'e', 's', 't' });
MessageBox.Show(GenericType as string);
The result:
Let's do the homework then. The following code should achieve what you want to do.
public class Parent
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child
{
string Name { get; set; }
int Age { get; set; }
}
public class DataController<TObject> where TObject : class
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : class
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : class
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
}
}
However, looking to your current example, you have two classes, Parent and Child. If I understand correctly, those are the only possibilities to be the type of TObject.
If that is the case, then the above code will explode if someone pass a string as the type parameter (when the execution reaches Activator.CreateInstance) - with a runtime exception (because string does not have a parameterless constructor):
To protect your code against that, we can inherit an interface in the possible classes. This will result in a compile time exception, which is preferable:
The code is as follows.
// Maybe you should give a better name to this...
public interface IAllowedParamType { }
// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child : IAllowedParamType
{
string Name { get; set; }
int Age { get; set; }
}
// Filter the interface on the 'where'
public class DataController<TObject> where TObject : class, IAllowedParamType
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : class, IAllowedParamType
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : class, IAllowedParamType
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
}
}
UPDATE
As RupertMorrish noted, you can still compile the following code:
public class MyObj : IAllowedParamType
{
public int Id { get; set; }
public MyObj(int id)
{
Id = id;
}
}
And that should still rise an exception, because you just removed the implicit parameterless constructor. Of course, if you know what you are doing, this is hard to happen, however we can forbidden this by using new() on the 'where' type filtering - while also getting rid of the Activator.CreateInstance stuff.
The entire code:
// Maybe you should give a better name to this...
public interface IAllowedParamType { }
// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
string Name { get; set; }
bool HasChildren { get; set; }
int Age { get; set; }
}
public class Child : IAllowedParamType
{
string Name { get; set; }
int Age { get; set; }
}
// Filter the interface on the 'where'
public class DataController<TObject> where TObject : new(), IAllowedParamType
{
protected DbContext _context;
public DataController(DbContext context)
{
_context = context;
}
}
public class FormController<TObject> where TObject : new(), IAllowedParamType
{
private DataController<TObject> _dataController;
public FormController(Button btn, DataController<TObject> dataController)
{
_dataController = dataController;
btn.Click += new EventHandler(btnClick);
}
private void btnClick(object sender, EventArgs e)
{
GenericForm<TObject> form = new GenericForm<TObject>();
form.ShowDialog();
}
}
public class GenericForm<TObject> : Form where TObject : new(), IAllowedParamType
{
public TObject GenericType { get; set; }
public GenericForm()
{
Paint += GenericForm_Paint;
}
private void GenericForm_Paint(object sender, EventArgs e)
{
MessageBox.Show(typeof(TObject).ToString());
// If you want to instantiate:
GenericType = new TObject();
}
}
I think you can add a new type argument to FormController:
public class FormController<TParent, TChild>
where TParent : class
where TChild : class
{
...
public void showSubForm(string className)
{
IFormInterface f2 = new FormInterface<TChild>();
f2.ShowDialog();
}
}
So as I understand it, you want a Form<T> to open upon some action in the MainForm, with your MainForm using a FormController, as a manager of all your forms, relaying the generic type information to your Form<T>. Furthermore, the instantiated object of your Form<T> class should request an instance of a DatabaseController<T> class from your FormController.
If that is the case, the following attempt might work:
MainForm receives a reference to the FormController instance upon constructor initialization or has another way to interact with the FormController, e.g. a CommonService of which both know, etc.
This allows MainForm to call a generic method of the FormController to create and show a new Form object:
void FormController.CreateForm<T> ()
{
Form<T> form = new Form<T>();
form.Show();
// Set potential Controller states if not stateless
// Register forms, etc.
}
with Form<T> along the lines of:
class Form<T> : Form where T : class
{
DatabaseController<T> _dbController;
Form(FormController formController)
{
_dbController = formController.CreateDatabaseController<T>();
}
}
Now you have a couple of ways for the Form to receive a DatabaseController instance:
1. Your Form<T> receives a reference of the FormController or has another way to communicate with it to call a method along the lines of:
DatabaseController<T> FormController.CreateDatabaseController<T> ()
{
return new DatabaseController<T>();
}
Your FormController does not need to be generic, otherwise you'd need a new FormController instance for every T there is. It just needs to supply a generic method.
Your Form<T> receives an instance of the DatabaseController from the FormController upon constructor initialization:
void FormController.CreateForm ()
{
Form form = new Form(new DatabaseController());
form.Show();
}
with Form<T> being:
class Form<T> : Form where T : class
{
DatabaseController<T> _dbController;
Form(DatabaseController<T> controller)
{
_dbController = controller;
}
}
3. Like with 2 but your Form<T> and DatabaseController<T> provide static FactoryMethods to stay true to the Single Responsibility Priciple. e.g.:
public class Form<T> : Form where T : class
{
private DatabaseController<T> _dbController;
public static Form<T> Create<T>(DatabaseController<T> controller)
{
return new Form<T>(controller);
}
private Form(DatabaseController<T> controller)
{
_dbController = controller;
}
}
4. You can also use an IoC Container to register and receive instances of a specific type at runtime. Every Form<T> receives an instance of the IoC Container at runtime and requests its corresponding DatabaseController<T>. This allows you to better manage the lifetime of your controller and form objects within the application.
Well i'm not gonna go into the details here and will only suffice to some blueprints.
In this scenario i'd use a combination of Unity constructor injection with a generic factory to handle the instantiation given the type in main form.
It's not that intricate, take a look at Unity documentation at
Dependency Injection with Unity
The reason for picking Unity out of all DI containers is it was part of Enterprise Library from Microsoft itself and now continues to live on as an independent library in the form of Nugget. a friend of mine has recently ported Unity to .Net core, too. Simply put, it's hands down the most elaborate container available.
As for the factory i believe it's necessary because you don't wanna create a concrete lookup for handling all possible types, so it clearly has to be a generic factory. I'd advise you to make your factory a singleton and put it in whole another project, thereby separating your UI project from the models and both party will communicate through this DI bridge. you can even take a step further and process your model types using assembly reflection.
sorry for being too general, but i really don't know how familiar are you with these patterns. It really worth taking some time and utilizing these patterns. in my humble opinion there is no escape from these maneuvers if you want a truly scalable software.
You can reach me in private if you're looking for hints on implementation of any of the above-mentioned strategies.
Try Factory method.
public interface IProvider
{
T GetObject<T>();
}
Top-level form:
public class TopLevelForm : Form
{
public TopLevelForm(IProvider provider):base()
{
_provider = provider;
}
private void ShowSecondForm()
{
var f2 = new SecondForm(provider);
f2.Show();
}
}
Second-level form:
public class SecondLevelForm : Form
{
public SecondLevelForm(IProvider provider):base()
{
_data = provider.GetObject<MyEntity>();
}
}
As for IProvider's implementation - there are plenty of methods, starting from the simpliest one, return new T();
Related
I have PhotoBase class
public abstract class PhotoBase
{
public string Path { get; set; }
}
And I have multiple derived classes, for example the path may indicate a location in the file system or an external url.
public class FilePhoto : PhotoBase {}
public class ExternalPhoto : PhotoBase {}
I want to load these photos, I have a PhotoLoader class like below:
public class PhotoLoader
{
public void Load(FilePhoto Photo)
{
// get the photo from file system
}
public void Load(ExternalPhoto Photo)
{
// download the photo from path
}
}
Now I want to load these photos, so I have to do:
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader)
{
_photoLoader = photoLoader;
}
public void ImportPhoto(PhotoBase photo)
{
if (photo is FilePhoto)
{
_photoLoader.Load(photo as FilePhoto);
}
if (photo is ExternalPhoto)
{
_photoLoader.Load(photo as ExternalPhoto);
}
}
}
I have several derived classes and I may add more photo types in the future. Is there a more elegant way that I could get rid of if conditions? Using factory pattern?
Another approach would be to have an abstract method Load on PhotoBase, that's then implemented by each subclass:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load();
}
public class FilePhoto : PhotoBase
{
public override void Load()
{
// load from file system
}
}
public class ExternalPhoto : PhotoBase
{
public override void Load()
{
// load from path
}
}
That way, you can simply call the Load method on a PhotoBase:
public class PhotoImporter
{
public void ImportPhoto(PhotoBase photo)
{
photo.Load();
}
}
The main advantage is that each subclass implements the Load method, and therefore you can add as many subclasses as you want, without worrying about forgetting to implement it.
The main drawback to this implementation is that the Load methods can't depend on other dependencies. So for example, if one day you need to load a photo from a database, you can't pass the DBContext.
Proper to way to implement it which will make code easy to maintain is as follows:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load(); // Have a abtract method
}
public class FilePhoto : PhotoBase {
public override void Load() { // Implement the abtract method
Console.WriteLine("FilePhoto");
}
}
public class ExternalPhoto : PhotoBase {
public override void Load() { // // Implement the abtract method
Console.WriteLine("ExternalPhoto");
}
}
public class PhotoLoader
{
private PhotoBase _PhotoBase;
public PhotoLoader(PhotoBase photoBase) { // Resolved by Dependency Injection
_PhotoBase = photoBase;
}
public void Load()
{
_PhotoBase.Load();
}
}
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader) // Resolved by Dependency Injection
{
_photoLoader = photoLoader;
}
public void ImportPhoto()
{
_photoLoader.Load();
}
}
You need to have Dependency Injection define so that specified implementation will get resolved and appropriate 'Load' method will be invoked.
void Main()
{
// Following dependency should be resolved by Dependency Injection
PhotoBase filePhotoBase = new FilePhoto();
PhotoLoader filePhotoLoader = new PhotoLoader(filePhotoBase);
PhotoImporter filePhotoImporter = new PhotoImporter(filePhotoLoader);
PhotoBase externalPhotoBase = new ExternalPhoto();
PhotoLoader externalPhotoLoader = new PhotoLoader(externalPhotoBase);
PhotoImporter externalPhotoImporter = new PhotoImporter(externalPhotoLoader);
filePhotoImporter.ImportPhoto(); // Shows output 'FilePhoto'
externalPhotoImporter.ImportPhoto(); // Shows output 'ExternalPhoto'
}
You can have as much implementation as you want, but you need to have proper instance of PhotoImporter by Dependency Injection. Everything else will work smoothly.
This is how I could do this:
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader)
{
_photoLoader = photoLoader;
}
public void ImportPhoto(PhotoBase photo)
{
var childType = photo.GetType();
dynamic childPhoto = Convert.ChangeType(photo, childType);
_photoLoader.Load(childPhoto);
}
}
You can simplify your ImportPhotoby using pattern matching:
public void ImportPhoto(photoBase photo)
{
switch(photo)
{
case FilePhoto filePhoto:
loader.Load(filePhoto);
break;
case ExternalPhoto externalPhoto:
loader.Load(externalPhoto);
break;
}
}
But this is only syntactic sugar and doesn't solve your real problem. What might help is a abstract Load method in your base class:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load();
}
You have to implement this method in your child classes. If you call the method, the correct implementation will be chosen.
Online demo: https://dotnetfiddle.net/LfkxBQ
I'm having difficulty inheriting from a generic class that is itself inherited from a generic singleton.
I am trying to make an inventory base class that is a singleton and derive different inventory types from this with different derived items.
Code has been simplified for brevity.
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static bool m_ShuttingDown = false;
private static object m_Lock = new object();
private static T m_Instance;
public static T Instance
{
get
{
if (m_ShuttingDown)
return null;
lock (m_Lock)
{
if (m_Instance == null)
{
m_Instance = (T)FindObjectOfType(typeof(T));
if (m_Instance == null)
{
var singletonObject = new GameObject();
m_Instance = singletonObject.AddComponent<T>();
singletonObject.name = typeof(T).ToString() + " (Singleton)";
DontDestroyOnLoad(singletonObject);
}
}
return m_Instance;
}
}
}
private void OnApplicationQuit()
{
m_ShuttingDown = true;
}
private void OnDestroy()
{
m_ShuttingDown = true;
}
}
public class Inventory<T> : Singleton<Inventory<T>> where T : Item
{
...
}
public class EquipmentInventory : Inventory<Equipment>
{
...
}
public class Item : ScriptableObject
{
public string Name = "Item";
}
public class Equipment : Item
{
public string Name = "Equipment";
}
I can't access the Instance;
private EquipmentInventory equipmentInventory;
private Inventory<Item> inventory;
public void Run()
{
var cachedInventory = Inventory<Item>.Instance; //returns null
var cachedEquipmentInventory = EquipmentInventory.Instance as EquipmentInventory; //returns null
}
Both statements return null.
The purpose of this, is that each inventory type will be a singleton and each type of inventory will be implent different item types, so that the base inventory will use the Item type, while the Equipment inventory will be implemented using the Equipment item type.
Here is an alternate method, which seems to solve this
public abstract class Inventory<T, TClass>
: Singleton<TClass> where TClass
: MonoBehaviour where T : Item
{
}
public class EquipmentInventory : Inventory<Equipment, EquipmentInventory>
{
}
I haven't fully tested this yet with actual code, but will update when I have tested it more thoroughly
Please assist.
The main issue is caused by
public class Inventory<T> : Singleton<Inventory<T>> : where T : Item { }
Here you "pass in" the generic type Iventory<T> into the Singleton even though later you explicitly inherit from that Inventory<T> class.
Here is one possible solution though it might seem like a bit strange workaround at first:
Make your Inventory take a second generic type, use the first one inside the class as needed and "forward" the second one to the Singleton<T> like e.g.
// Note how for the limitation via where you still can use the generic type
// which makes sure no other MonoBehaviour can be passed to TSelf by accident
public class Inventory<TItem, TSelf> : Singleton<TSelf> where TItem : Item where TSelf : Inventory<TItem,TSelf>
{
public TItem reference;
private void Awake()
{
if (!reference) reference = ScriptableObject.CreateInstance<TItem>();
}
}
Then in the implementation your pass additionally in your own final (non-generic) type so it can be properly "forwarded" to the Singleton<T> like e.g.
public class EquipmentInventory : Inventory<Equipment, EquipmentInventory> { }
Note that anyway this class has to be in a separated file called EquipmentInventory.cs otherwise it won't work as component in Unity.
This works now since now you explicitly pass in the type EquipmentInventory for TSelf which is then forwarded to the Signleton<T> so the return type of Instance is explicitly EquipmentInventory.
In general get used to rather have one script file for each individual class/type.
Additionally I would slightly alter your fields in Item and Equipment like e.g.
[CreateAssetMenu]
public class Item : ScriptableObject
{
[SerializeField] private string _name;
public string Name => _name;
private void Awake()
{
_name = GetName();
}
protected virtual string GetName()
{
return nameof(Item);
}
}
and
[CreateAssetMenu]
public class Equipment : Item
{
protected override string GetName()
{
return nameof(Equipment);
}
}
And this is how it looks like e.g.
public class Example : MonoBehaviour
{
public EquipmentInventory equipmentInventory;
[ContextMenu("Run")]
public void Run()
{
equipmentInventory = EquipmentInventory.Instance;
}
}
I made a class which requires the public default constructor but
that is never called; instead another constructor is used at DataGrid.AddingNewItem.
I'd like to tell developers that the default constructor is not for their use.
Is there an attribute which suits the purpose?
I had checked DebuggerNonUserCode and MethodImplAttribute with MethodImplAttributes.InternalCall but not sure that's the proper approach.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.dataGrid1.CanUserAddRows = true;
var list = new List<RowX>();
this.dataGrid1.ItemsSource = CollectionViewSource.GetDefaultView(list);
this.dataGrid1.AddingNewItem += (s, e) => e.NewItem = new RowX("ABC");
}
}
public class RowX
{
public RowX()
{
//this is not used. but CollectionView require this to be public or
//CanUserAddRows doesn't work.
}
public RowX(object o)
{
//this is the actual ctor.
}
public string Text { get; set; }
}
Mark it private
class Foo
{
private Foo() {}
}
You can give your constructor an access modifier.
private This means it can only be called from another constructor in that class.
public class PrivateClass
{
//Only from inside this class:
private PrivateClass()
{
}
public static PrivateClass GetPrivateClass()
{
//This calls the private constructor so you can control exactly what happens
return new PrivateClass();
}
}
internal This means only code in the same assembly (i.e. from inside your library) can access it.
public class InternalClass
{
//Only from within the same assembly
internal InternalClass(string foo)
{
}
}
Using C# .NET 4.0, Visual Studio 2010.
Well at the moment I'm looking into de-coupling of Classes and using interfaces.
I've implemented a Solution from another post to test if I could get it working but unfortunately I have never used an interface ever.
So here's the basics of what I have:
Form1:
partial class Form1 : InterfacePareto
{
public string myTest
{
get { return herpTxt.Text; }
set { herpTxt.Text = value; }
}
}
Interface:
interface InterfacePareto
{
string myTest { get; set; }
}
MyWorkingOutClass:
Class MyWorkingOutClass
{
private readonly InterfacePareto pare;
public MyWorkingOutClass(InterfacePareto pare)
{
this.pare = pare;
}
private void Testtime()
{
string firstName = pare.myTest;
pare.myTest = firstName + " extra";
}
}
The purpose:
The plan at the moment is to get the text from the forms textbox. Then pass it to the working class. The working class then does whatever calculations etc needed, then passes the result back to the forms textbox.
My question is, is my code along the right tracks. If yes, then what am I missing/doing wrong? Or if anyone thinks this is not the right way of achieving what I need, do they have any suggestions?
Many thanks!
I've just tested code and this works fine for me:
public partial class MainForm :Form, InterfacePareto //My main form inheriting Form class and interface
{
public MainForm()
{
InitializeComponent();
}
public string myTest
{
get { return herpTxt.Text; }
set { herpTxt.Text = value; }
}
private void button1_Click(object sender, EventArgs e)
{
//On button click create MyWorkingOutClass instance and pass MainForms instance
MyWorkingOutClass mc = new MyWorkingOutClass(this);
//After this line text box content will change
mc.Testtime();
}
}
//Changed modifier to public
public interface InterfacePareto
{
string myTest { get; set; }
}
//Changed modifier to public
public class MyWorkingOutClass
{
private readonly InterfacePareto pare;
public MyWorkingOutClass(InterfacePareto pare)
{
this.pare = pare;
}
//Changed modifier to public
public void Testtime()
{
string firstName = pare.myTest;
pare.myTest = firstName + " extra";
}
}
This should work fine.
There is one issue you will get when the MyWorkingOutClass does its work on a different thread than the UI thread.
To solve that you might want to change the implementation on the form to switch to the UI thread.
I'm trying out an example of using Domain Events to notify of when something has happened in a system (borrowed from here and here).
I'm really close to getting the code working how I want, however, I've hit a bit of a brick wall. Here is my DomainEvents class:
public static class DomainEvents
{
[ThreadStatic]
private static IList<IEventHandler<IDomainEvent>> Actions;
public static void Register<T>(IEventHandler<T> callback) where T : IDomainEvent
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback); // <---- Problem here, since I can't add callback to the collection.
}
public static void ClearCallbacks()
{
Actions = null;
}
public static void Raise<T>(T args) where T : IDomainEvent
{
if (Actions == null)
{
return;
}
foreach (var action in Actions)
{
if (action is IEventHandler<T>)
{
((IEventHandler<T>)action).Handle(args);
}
}
}
The above won't compile because Actions.Add cannot accept callback since it's a IEventHandler<T> type rather then a IEventHandler<IDomainEvent> type. Here's some more code to clarify.
This is called from my console application:
DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());
CustomerHasUnpaidDuesEventHandler implements IEventHandler<CustomerHasUnpaidDuesEvent>, where CustomerHasUnpaidDuesEvent implements IDomainEvent.
public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
{
public IEmailSender EmailSender { get; set; }
public void Handle(CustomerHasUnpaidDuesEvent #event)
{
this.EmailSender.SendEmail(#event.Customer.EmailAddress);
}
}
public class CustomerHasUnpaidDuesEvent : IDomainEvent
{
public CustomerHasUnpaidDuesEvent(Customer customer)
{
this.Customer = customer;
}
public Customer Customer { get; set; }
}
This is what I don't get - if CustomerHasUnpaidDuesEvent implements IDomainEvent, then why is the call to Actions.Add failing? How can I resolve this?
EDIT:
To make things clearer, here is entire code for my test app:
class Program
{
static void Main()
{
DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());
var c = new Customer();
c.EmailAddress = "test#dfsdf.com";
c.CheckUnpaidDues();
}
}
public interface IEventHandler<in T> where T : IDomainEvent
{
void Handle(T args);
}
public interface IEmailSender
{
void SendEmail(string emailAddress);
}
public interface IDomainEvent
{
}
public static class DomainEvents
{
[ThreadStatic]
private static IList<IEventHandler<IDomainEvent>> Actions;
public static void Register<T>(IEventHandler<T> callback) where T: IDomainEvent
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback);
}
public static void ClearCallbacks()
{
Actions = null;
}
public static void Raise<T>(T args) where T : IDomainEvent
{
if (Actions == null)
{
return;
}
foreach (IEventHandler<T> action in Actions)
{
(action).Handle(args);
}
}
}
public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
{
public IEmailSender EmailSender { get; set; }
public void Handle(CustomerHasUnpaidDuesEvent #event)
{
this.EmailSender.SendEmail(#event.Customer.EmailAddress);
}
}
public class CustomerHasUnpaidDuesEvent : IDomainEvent
{
public CustomerHasUnpaidDuesEvent(Customer customer)
{
this.Customer = customer;
}
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string EmailAddress { get; set; }
public bool HasUnpaidDues { get; set; }
public void CheckUnpaidDues()
{
HasUnpaidDues = true;
DomainEvents.Raise(new CustomerHasUnpaidDuesEvent(this));
}
}
Cheers.
Jas.
There is no need for your Register method to be generic:
public static void Register(IEventHandler<IDomainEvent> callback)
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback);
}
Edit:
The problem is that in order to have IEventHandler<CustomerHasUnpaidDuesEvent> to be in the list of IEventHandler<IDomainEvent>s, we need T to be a covariant template parameter in IEventHandler<T> (which is declared as IEventHandler<out T>). However in order to allow the function Handle(T arg), we need T to be contravariant. So strictly this way won't work. Imagine: if we really could insert an IEventHandler<CustomerHasUnpaidDuesEvent> into a list of IEventHandler<IDomainEvent>s, than someone might try to call Handle with the argument of some type which derives from IDomainEvent but is not a CustomerHasUnpaidDuesEvent! This should be impossible to do.
The solution is that we don't need the exact type at Register, so we can keep a reference to a generic base interface. The implementation is here: http://ideone.com/9glmQ
Old answer is not valid, kept below for consistency.
Maybe you need to declare IEventHandler to accept T as a covariant type?
interface IEventHandler<in T> where T: IDomainEvent
{
void Handle();
// ...
}
Edit: surely CustomerHasUnpaidDuesEvent is an IDomainEvent, but you need IEventHandler<CustomerHasUnpaidDuesEvent> to be a IEventHandler<IDomainEvent>. This is exactly what covariance does. In order to allow that, your template parameter in IEventhandler must be declared covariant (<in T> instead of just <T>).