C# Handle Event - c#

I am trying to make sort of a library and i am trying to grasp how can i implement it they way i want.
I created a minimalist example to show you what i am trying to do.
using System;
namespace example
{
public class Car
{
public int Price;
public string ModelName;
public Boolean Sold;
public delegate void SellEventHandler(string str);
public static event SellEventHandler _OnSell;
public void OnSell(string str)
{
Console.WriteLine("event was fired");
}
public Car(int price, string modelname)
{
Price = price;
ModelName = modelname;
Sold = false;
_OnSell = OnSell;
}
}
public class Program
{
static void Main()
{
Car _car = new Car(6000, "audi");
_car._OnSell += Car_OnSell;
}
public void Car_OnSell(string message)
{
Console.WriteLine(message);
}
}
}
Even though i haven't implemented when the event will be invoked ( it should be invoked when the Sold property of the _car changes ), i want to execute the OnSell(string str) method of the Car class ( prints "event was fired" ) and after that, i want to execute the Car_OnSell function ( see code _car.OnSell += Car_OnSell )
Hopefully you get the idea of what i am trying to do here. Right now the error i get is Member 'example.Car._OnSell' cannot be accessed with an instance reference; qualify it with a type name instead on the line _car.OnSell += Car_OnSell;. However i am not sure if i am going in the right direction with this at all.

I think I understand what you're doing, and here's how I would do it.
Don't hook up an event in your class. Instead, create a 'Sell' method that does whatever the class would normally do (like set Sold == true), but first check if the client hooked up your _OnSell event, and fire that first. You may want to provide some way for the client to cancel the sale in the _OnSell event as well.
You also need to make your Car_OnSell static, since you're hooking it up from a static method (Main). This is because a non-static method requires a class instance to access it.
Here's an example:
static void Main()
{
var car = new Car(6000, "audi");
car._OnSell += Car_OnSell;
car.Sell(string.Format("Selling the car: {0}", car.ModelName));
}
public static void Car_OnSell(string message)
{
Console.WriteLine(message);
}
public class Car
{
public int Price { get; set; }
public string ModelName { get; set; }
public Boolean Sold { get; set; }
public delegate void SellEventHandler(string str);
public event SellEventHandler _OnSell;
public void Sell(string str)
{
if (_OnSell != null)
{
_OnSell(str);
}
this.Sold = true;
}
public Car(int price, string modelname)
{
Price = price;
ModelName = modelname;
Sold = false;
}
}

Related

C# calling member function with delegate

because of this post I create a new question to make my probleme more clear. I have a class with a next class member, so there will be a daisy chain of class instances. A function in my class calls another member function or all instances in the chain.
c ++ has a resonable solution for this problem. In C# I tried it with a delegate. I made a short program to show what I mean.
class Program {
static void Main(string[] args)
{
DaisyChain TestClass = new DaisyChain(1);
TestClass.AddClass(new DaisyChain(2));
TestClass.AllprintID();
}
}
class DaisyChain {
private int ClassID;
private DaisyChain NextClass;
public DaisyChain(int ID) {ClassID = ID; }
public void AddClass(DaisyChain newClass) {
if (NextClass == null) {
NextClass = newClass;
} else {
NextClass.AddClass(newClass);
}
}
public void AllprintID() {
DoForEach(this.printID);
}
public delegate void doFunc();
public void DoForEach (doFunc aMemberFunc) {
aMemberFunc();
if (NextClass != null) {
NextClass.DoForEach(aMemberFunc);
}
}
public void printID() {
Console.WriteLine(ClassID);
}
};
This example do not work correct, because the class instance is not part of the function call.
I can add a class argumnet to my member function and chang the delegate,
public void printID(DaisyChain me) {
Console.WriteLine(me.ClassID);
}
but then the function will be static and no longer usable in the normal way.
I would be happy if ther another solution.
The delegate type should have an extra argument, since you want to call printID on different objects. You can either add one to doFunc, or just use the built in Action<T> delegate type.
public void DoForEach (Action<DaisyChain> aMemberFunc) {
aMemberFunc(this);
if (NextClass != null) {
NextClass.DoForEach(aMemberFunc);
}
}
When calling DoForEach, you can either pass a lambda expression:
public void AllprintID() {
DoForEach(x => x.printID());
}
Or if you really like the method group syntax for some reason, write a local function printID:
public void AllprintID() {
void PrintID(DaisyChain chain) {
chain.PrintID();
}
DoForEach(PrintID);
}
// method names should start with a capital letter :)
public void PrintID() {
Console.WriteLine(ClassID);
}
Other code can still call PrintID as usual - code outside AllprintID won't even notice the local function.
You are trying to reinvent the wheel. Check LinkedList and LinkedListNode in the documentation. Here is an example to get you on the way:
var daisyChain = new DaisyChain();
daisyChain.Add(1);
daisyChain.Add(2);
class DaisyChain: LinkedList<DaisyChainLink>
{
public void Add(int id) => AddLast(new LinkedListNode<DaisyChainLink>(new DaisyChainLink(id)));
public void Print()
{
var link = this.First;
link?.Value.Print();
while (null != link?.Next)
{
link = link.Next;
link?.Value.Print();
}
}
}
class DaisyChainLink
{
public DaisyChainLink(int id)
{
Id = id;
}
public int Id { get; }
public void Print() => Console.WriteLine(Id);
}

Inherit in generic classes C#

My brain is gonna to explode. :) So I would like to get help from you.
Please, think about my question like about just programmer puzzle. (Actually. perhaps it is very easy question for you, but not for me.)
It is needed to create array of objects. For example List where T is class. (I will describe Class T below). Also it is needed create “container” that will contain this array and some methods for work with this array. For example Add(), Remove(int IndexToRemove).
Class T must have field "Container", this way each elements of our array would be able to know where is it contained and has access its container's fields and methods. Notice, that in this case Class T should have type parameter. Indeed, it is not known beforehand which container's type is used.
Let us denote this class container as A and class element (class T) as AUnit.
Code:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a.Units[0].SomeField +=100;
Console.ReadKey();
}
}
class A
{
public List<AUnit> Units;
public A()//ctor
{
Units = new List<AUnit>();
}
public void Add()
{
this.Units.Add(new AUnit(this));
}
}
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
}
Public fields should be protected or private of course, but let think about this later.
You can ask “why we create public A Container field in AUnit”? We create field public string Name{get;private set;} (actually property but nevermind). And also we would like to be able to change value of this field for example method [Class AUnit] public bool Rename(string newName)();. The main idea of this method is changing Name field only that case if no one element in array (public List Units; ) has the same name like newName. But to achieve this, Rename method has to have access to all names that is currently used. And that is why we need Container field.
Code of extended version AUnit
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.Units)
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
Ok. If you still read it let's continue. Now we need to create Class B and class BUnit which will be very similar like Class A and Class Aunit. And finally the main question of this puzzle is HOW WE CAN DO IT? Of course, I can CopyPaste and bit modify A and AUnit and create this code.
class B
{
public List<BUnit> Units; //Only Type Changing
public B()//ctor Name changing...
{
Units = new List<BUnit>();//Only Type Changing
}
public void Add()
{
this.Units.Add(new BUnit(this));//Only Type Changing
}
}
class BUnit
{
public int SomeField;
public B Container;//Only Type Changing
public string Name { get; private set; }
public A a; //NEW FIELD IS ADDED (just one)
public BUnit(B container) //Ctor Name and arguments type changing
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
this.a=new A(); //New ROW (just one)
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (BUnit unt in this.Container.Units) //Only Type Changing
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
And I can to use this classes this way.
static void Main(string[] args)
{
B b = new B();
b.Add();
b.Units[0].a.Add();
b.Units[0].a.Units[0].SomeField += 100;
bool res= b.Units[0].a.Units[0].Rename("1");
res = b.Units[0].a.Units[0].Rename("1");
Console.ReadKey();
}
This construction is can be used to create “non-homogeneous trees”.
Help, I need somebody help, just no anybody…. [The Beatles]
I created B and BUnit using CopyPaste.
But how it can be done using “macro-definitions” or “Generic”, inherit or anything else in elegant style? (C# language)
I think that there is no reason to describe all my unsuccessful attempts and subquestions. Already topic is too long. : )
Thanks a lot if you still read it and understand what I would like to ask.
You need to implement a base type, lets call it UnitBase, with all common functionality. I'd structure your code the following way:
Create an interface for your container, this way you can change implementation to more performant solutions without modifying the elements you will be adding to the container.
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
}
Following the idea stated in 1, why not make the search logic belong to the container? It makes much more sense, as it will mostly depend on how the container is implemented:
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
bool Contains(string name);
}
A specific implementation of IContainer could be the following:
public class Container : IContainer
{
public Container()
{
list = new List<UnitBase>();
}
private List<UnitBase> list;
public Q Add<Q>() where Q: UnitBase, new()
{
var newItem = Activator.CreateInstance<Q>();
newItem.SetContainer(this);
list.Add(newItem);
return newItem;
}
public IEnumerable<UnitBase> Units => list.Select(i => i);
public bool Contains(string name) =>
Units.Any(unit => unit.Name == name);
}
Create a base class for your AUnit and BUnit types condensing all common functionality:
public abstract class UnitBase
{
protected UnitBase()
{
}
public IContainer Container { get; private set; }
public int SomeField;
public string Name { get; private set; }
public void SetContainer(IContainer container)
{
Container = container;
}
public bool Rename(String newName)
{
if (Container.Contains(newName))
return false;
this.Name = newName; //No need to use String.Copy
return true;
}
}
Implement your concrete types:
public class BUnit : UnitBase
{
public int SpecificBProperty { get; private set; }
public BUnit()
{
}
}
Shortcomings of this approach? Well, the container must be of type <UnitBase>, I've removed the generic type because it really wasn't doing much in this particular case as it would be invariant in the generic type.
Also, keep in mind that nothing in the type system avoids the following:
myContainer.Add<BUnit>();
myContainer.Add<AUnit>();
If having two different types in the same container is not an option then this whole set up kind of crumbles down. This issue was present in the previous solution too so its not something new, I simply forgot to point it out.
InBetween , I am very thankful to you for your advices. Actually I can't say that I understood your answer in full, but using your ideas I have done what I want.
Looks like my variant works well. However I would like to hear your (and everyone) opinions about code described below. The main goal of this structure is creating non-homogeneous trees. So could you estimate it from this side.
First of all. We need to create interfaces for both classes. We describe there all "cross-used" functions.
public interface IUnit<T>
{
string Name { get;}
void SetContainer(T t);
bool Rename(String newName);
}
public interface IContainer
{
bool IsNameBusy(String newName);
int Count { get; }
}
Next. Create Base for Unit Classes for future inheritance. We will use in this inheritors methods from Container Base so we need generic properties and IUnit interface.
class UnitBase<T> : IUnit<T> where T : IContainer
Unfortunately I don't know yet how to solve the problem with Constructor parameters. That is why I use method
SetContainer(T container).
Code:UnitBase
class UnitBase<T> : IUnit<T> where T : IContainer
{
protected T Container;
public string Name { get; private set; }
public UnitBase()
{
this.Name = "Default";
}
public void SetContainer(T container)
{
this.Container = container;
}
public bool Rename(String newName)
{
bool res = Container.IsNameBusy(newName);
if (!res) this.Name = String.Copy(newName);
return !res;
}
}
Next. Create ContainerBase
ContainerBase should:
1) has IContainer interface.
2)has information about what it will contain:
... where U : IUnit<C>, new()
3)and .... has information about what itself is. This information we need to pass as parameter to SetContainer() method.
Code ContainerBase:
class ContainerBase<U, C> : IContainer //U - Unit Class. C-Container Class
where U : IUnit<C>, new()
where C : ContainerBase<U, C>
{
protected List<U> Units;
public U this[int index] { get { return Units[index]; } }
public ContainerBase()//ctor
{
this.Units = new List<U>();
}
public void Add()
{
this.Units.Add(new U());
this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C>
}
public bool IsNameBusy(String newName)
{
bool res = false;
foreach (var unt in this.Units)
{
if (unt.Name == newName)
{
res = true;
break;
}
}
return res;
}
public int Count { get { return this.Units.Count; } }
}
Cast ((TContainer)(this)) may be is a bit strange. But using ContainerBase we always should use NewInheritorContainer. So this cast is just do nothing…looks like...
Finally. This classes can be used like in this example.
class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}}
class SheetUnit : UnitBase<SheetContainer>
{
public CellContainer Cells;
public PictureContainer Pictures;
public SheetUnit()
{
this.Cells = new CellContainer();
this.Pictures = new PictureContainer();
}
}
class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } }
class CellUnit : UnitBase<CellContainer>
{
public string ValuePr;//Private Field
private const string ValuePrDefault = "Default";
public string Value//Property for Value
{
//All below are Just For Example.
get
{
return this.ValuePr;
}
set
{
if (String.IsNullOrEmpty(value))
{
this.ValuePr = ValuePrDefault;
}
else
{
this.ValuePr = String.Copy(value);
}
}
}
public CellUnit()
{
this.ValuePr = ValuePrDefault;
}
}
class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } }
class PictureUnit : UnitBase<PictureContainer>
{
public int[,] Pixels{get;private set;}
public PictureUnit()
{
this.Pixels=new int[,]{{10,20,30},{11,12,13}};
}
public int GetSizeX()
{
return this.Pixels.GetLength(1);
}
public int GetSizeY()
{
return this.Pixels.GetLength(0);
}
public bool LoadFromFile(string path)
{
return false;
}
}
static void Main(string[] args)
{
SheetContainer Sheets = new SheetContainer();
Sheets.Add();
Sheets.Add();
Sheets.Add();
Sheets[0].Pictures.Add();
Sheets[1].Cells.Add();
Sheets[2].Pictures.Add();
Sheets[2].Cells.Add();
Sheets[2].Cells[0].Value = "FirstTest";
bool res= Sheets[0].Rename("First");//res=true
res=Sheets[2].Rename("First");//res =false
int res2 = Sheets.Count;
res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13
res2 = Sheets[2].Pictures.Count;//1
res2 = Sheets[1].Pictures.Count;//0
res2 = Sheets[0].Pictures[0].GetSizeX();//3
Console.ReadKey();
}
Looks like it works like I want. But I didn’t test it full.
Let me say Thank you again, InBetween.

Moving dynamic method call to class library causes a RuntimeBinderException in C#

I was reading an interesting article using of DataFlow + dynamic method invocation to make an Actor model in C#. Here is the completed example verbatim.
using System;
using System.Threading.Tasks.Dataflow;
namespace ConsoleApplication
{
public abstract class Message { }
public abstract class Actor
{
private readonly ActionBlock<Message> _action;
public Actor()
{
_action = new ActionBlock<Message>(message =>
{
dynamic self = this;
dynamic mess = message;
self.Handle(mess);
});
}
public void Send(Message message)
{
_action.Post(message);
}
}
class Program
{
public class Deposit : Message
{
public decimal Amount { get; set; }
}
public class QueryBalance : Message
{
public Actor Receiver { get; set; }
}
public class Balance : Message
{
public decimal Amount { get; set; }
}
public class AccountActor : Actor
{
private decimal _balance;
public void Handle(Deposit message)
{
_balance += message.Amount;
}
public void Handle(QueryBalance message)
{
message.Receiver.Send(new Balance { Amount = _balance });
}
}
public class OutputActor : Actor
{
public void Handle(Balance message)
{
Console.WriteLine("Balance is {0}", message.Amount);
}
}
static void Main(string[] args)
{
var account = new AccountActor();
var output = new OutputActor();
account.Send(new Deposit { Amount = 50 });
account.Send(new QueryBalance { Receiver = output });
Console.WriteLine("Done!");
Console.ReadLine();
}
}
}
This works as intended. Moving Actor & Message classes into a new class library and referencing properly causing an issue. When run it throws a RuntimeBinderException on the dynamic self.Handle(mess); within the Actor constructor saying Actors.Actor does not contain a definition for 'Handle'. Are there limitations to dynamic method calls I can't seem to find in the MSDN or syntax magic I missing to do this from a separate class library?
The original author got back to me.
Hi,
The problem is that you have declared your messages and actors inside
the internal NotWorkingProgram class.
class NotWorkingProgram // no access modifier! Default is 'internal' {
public class Deposit : Message
...
public class AccountActor : Actor
{
public void Handle(Deposit message)
...
} }
When you run the program the runtime tries to find a method named
'Handle' with a parameter of typ 'Deposit'. It can't find anything
because the AccountActor class is not visible from the Actors Project.
It is hidden inside the invisible NotWorkingProgram. If you make the
NotWorkingProgram class public (or move the Deposit and AccountActor
classes outside) it works!
Regards Johan
I'm leaving this here cause the RuntimeBinderException doesn't give much info, let alone any hint of class/method privacy being a possible root

Observable.From for future events instead of subject

how can i implement this situation in RX without using subject. I've read a lot, and I just can't seem to figure it out
public class Member
{
public int Id { get; private set; }
public string Email { get; private set; }
public Member(string email)
{
this.Email = email;
}
}
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
memberAdded.OnNext(member);
}
private Subject<Member> memberAdded = new Subject<Member>();
public IObservable<Member> MemberAdded { get { return memberAdded.AsObservable(); } }
}
public class MemberController
{
public void Create(Member item)
{
var repository = new MemberRepository();
var subs = repository.MemberAdded.Subscribe(x => SendMail(x));
repository.AddMember(item);
}
private void SendMail(Member value)
{
// send welcome mail
}
}
I've don't know how to initialize the IObservable MemberAdded because it is always null if it doesn't have the Subject backer nor do I know how to later call the OnNext at from a later function.
Lastly, is it a problem to have the observables as static properties and all the subscription code in one place?
The way I have implemented something similar is to expose a normal C# event MemberAdded on my MemberRepository. You can then use Observable.FromEvent or Observable.FromEventPattern (the difference is here) to subscribe to the event something like this:
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
if (MemberAdded != null)
MemberAdded(new MemberEventArgs(member, MemberEvent.Add));
}
public event EventHandler<MemberEventArgs> MemberAdded;
}
...
Observable.FromEventPattern<MemberEventArgs>(h => memberRepository.MemberAdded += h,
h => memberRepository.MemberAdded -= h)
.Select(e => e.Member)
.Subscribe(m => Console.WriteLine("Member "+m+" added!));
In regard to your second question, you should avoid static properties - consider using something like the Event Aggregator pattern instead

Refactoring a list of objects to implement a business rule

I need to refactor the following class:
public interface IEmployee
{
int VacationWeeks { get; }
int YearsWithCompany { set; get; }
double Salary { set; get; }
}
public class Employee : IEmployee
{
private readonly int vacationWeeks;
public Employee(int vacationWeeks)
{
this.vacationWeeks = vacationWeeks;
}
public int VacationWeeks
{
get { return vacationWeeks; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
I need to make sure that VacationWeeks depends only on YearsWithCompany, and I am loading the mapping from the database. So far I have come up with this:
public class EmployeeNew : IEmployee
{
private Dictionary<int,int> vacationWeeksTable;
public EmployeeNew(Dictionary<int, int> vacationWeeksTable)
{
this.vacationWeeksTable = vacationWeeksTable;
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
This class implements what I want, but it still has one vulnerability: different instances of EmployeeNew in the same collection may have been created with different instances of vacationWeeksTable.
All instances of EmployeeNew in the same collection must refer to the same vacationWeeksTable.
The application I am refactoring uses lots of List all over the system, and we need to be able to modify YearsWithCompany and Salary, yet to guarantee that only one vacationWeeksTable is used per List. These lists are iterated several times; its elements are modified in each iteration.
Here is my imperfect solution. Suggestions are welcome:
// this class does two things, which I do not like
public class EmployeeList : IEnumerable<IEmployee>, IEmployee
{
private Dictionary<int, int> vacationWeeksTable;
private List<EmployeeSpecificData> employees;
private int currentIndex;
private EmployeeSpecificData CurrentEmployee
{
get { return employees[currentIndex]; }
}
public IEnumerator<IEmployee> GetEnumerator()
{
for (currentIndex = 0; currentIndex < employees.Count; currentIndex++)
{
yield return this;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
// this is ugly repetitive code I don't like
public int YearsWithCompany
{
get { return CurrentEmployee.YearsWithCompany; }
set { CurrentEmployee.YearsWithCompany = value; }
}
// this is ugly repetitive code I don't like
public double Salary
{
get { return CurrentEmployee.Salary; }
set { CurrentEmployee.Salary = value; }
}
}
I use the following to create and init some of the classes that need default and shared behaviour. Maybe if you can refactor it will help:
It is some form of the Factory and FlyWeight patterns combined (the flyweight part can be removed in your scenario), which in addition has a concept of class Type shared handlers.
I simplified and removed some stuff that you wont need but there is more to remove, I added comments.
Usage would be: (app init)
Dictionary<int,int> vacationWeeksTable = new Dictionary<int,int>();
// fill the table
Factory<Employee>.Init(vacationWeeksTable);
The whenever you create a Employee class:
// remove grouping in the factory class to remove this null
Employee em = Factory<Employee>.Create(null);
It takes only a WeakReference to the classes so you don't have to worry about GC.
Each employee will have the shared vacationWeeksTable setup on creation, without the possibility to change it after from outside if not using the factory class.
You could change the vacation table for all running instances of Employee at any moment in the runtime of the app with:
// this will call the method registered for SetInitialdata on all instances of Employee classes.
// again remove grouping to remove that null
Factory<Employee>.Call(EventHandlerTypes.SetInitialData, null, vacTable);
Sample implementation of Employee:
class Employee : IBaseClass
{
private Dictionary<int, int> vacationWeeksTable;
public virtual void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> register)
{
group = 0; // disable different groups
register(new Key<Employee, int>(0), group, EventHandlerTypes.SetInitialData, SetVacationWeeksTable);
}
public virtual void RegisterSharedData(Action<IKey, object> regData)
{
// remove this from factory and interface, you probably dont need it
// I have been using it as a FlyWeight data store for classes.
}
private void SetVacationWeeksTable(object sender, SharedEventArgs e)
{
vacationWeeksTable = e.GetData<Dictionary<int, int>>();
}
}
Code pattern Implementation:
IBaseClass : interface that each of my classes that are creatable through a factory implement
public enum EventHandlerTypes
{
SetInitialData // you can add additional shared handlers here and Factory<C>.Call - it.
}
public class SharedEventArgs : EventArgs
{
private object data;
public SharedEventArgs(object data)
{
this.data = data;
}
public T GetData<T>()
{
return (T)data;
}
}
public interface IBaseClass
{
void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> regEvent);
void RegisterSharedData(Action<IKey, object> regData);
}
Utility generic classes:
public interface IKey
{
Type GetKeyType();
V GetValue<V>();
}
public class Key<T, V> : IKey
{
public V ID { get; set; }
public Key(V id)
{
ID = id;
}
public Type GetKeyType()
{
return typeof(T);
}
public Tp GetValue<Tp>()
{
return (Tp)(object)ID;
}
}
public class Triple<T, V, Z>
{
public T First { get; set; }
public V Second { get; set; }
public Z Third { get; set; }
public Triple(T first, V second, Z third)
{
First = first;
Second = second;
Third = third;
}
}
Factory class with slight modification to handle your scenario:
public static class Factory<C> where C : IBaseClass, new()
{
private static object initialData;
private static Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>> handlers = new Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>>();
private static Dictionary<IKey, object> data = new Dictionary<IKey, object>();
static Factory()
{
C newClass = new C();
newClass.RegisterSharedData(registerSharedData);
}
public static void Init<IT>(IT initData)
{
initialData = initData;
}
public static Dt[] GetData<Dt>()
{
var dataList = from d in data where d.Key.GetKeyType() == typeof(Dt) select d.Value;
return dataList.Cast<Dt>().ToArray();
}
private static void registerSharedData(IKey key, object value)
{
data.Add(key, value);
}
public static C Create(int? group)
{
C newClass = new C();
newClass.RegisterSharedHandlers(group, registerSharedHandlers);
// this is a bit bad here since it will call it on all instances
// it would be better if you can call this from outside after creating all the classes
Factory<C>.Call(EventHandlerTypes.SetInitialData, null, initialData);
return newClass;
}
private static void registerSharedHandlers(IKey subscriber, int? group, EventHandlerTypes type, Action<object, SharedEventArgs> handler)
{
handlers.Add(subscriber, new Triple<EventHandlerTypes, int, WeakReference>(type, group ?? -1, new WeakReference(handler)));
}
public static void Call<N>(EventHandlerTypes type, int? group, N data)
{
Call<N>(null, type, group, data);
}
public static void Call<N>(object sender, EventHandlerTypes type, int? group, N data)
{
lock (handlers)
{
var invalid = from h in handlers where h.Value.Third.Target == null select h.Key;
// delete expired references
foreach (var inv in invalid.ToList()) handlers.Remove(inv);
var events = from h in handlers where h.Value.First == type && (!#group.HasValue || h.Value.Second == (int)#group) select h.Value.Third;
foreach (var ev in events.ToList())
{
// call the handler
((Action<object, SharedEventArgs>)ev.Target)(sender, arg);
}
}
}
}
Make a class which contains a Dictionary. Creating or getting instance of this new class will load the dictionary in a consistent way. Then your BOs can take an instance of the class, thus ensuring they're all using the same data (because the class containingthe list knows how to load itself with the proper set of data).

Categories