New programmer, using C# and VB 2015, first time post so please be gentle!
Basically I am using Dictionary for the first time, and I am trying to access the method useMedPack() which inside my MedPack class, which is a child of Item, which is being created when adding it to my Dictionary. The problem is that it says:
****EDIT**** I feel like I should have added the Item class in first time round (now added to the bottom). Following some awesome suggestions, I casted using ((MedPack)inventory["MedPack"]).useMedPack(); and it now works as intended! Although some of the feedback has been great and I have learned a lot from everyones suggestions! :)
'Item' does not contain a definition for useMedPack();.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Dictionary<String, Item> inventory = new Dictionary<String, Item>();
inventory.Add("MedPack", new MedPack("MedPack", 1));
MedPack.pickUpMedPack(inventory);
//THIS IS THE PROBLEM inventory["MedPack"].useMedPack();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
}
namespace ConsoleApplication1
{
class MedPack : Item
{
private int healthReturn = 10;
public MedPack() : base()
{
}
public MedPack(String itemName, int itemQuantity) : base(itemName, itemQuantity)
{
}
public void useMedPack()
{
decreaseQuantity(1);
}
public static void pickUpMedPack(Dictionary<String, Item> inventory)
{
if (!inventory.ContainsKey("MedPack"))
{
inventory.Add("MedPack", new MedPack("MedPack", 1));
Console.WriteLine("You found a MedPack! It was added to the inventory");
}
else
{
inventory["MedPack"].increaseQuantity(1);
Console.WriteLine("You found ANOTHER MedPack! It was added to the inventory");
}
}
}
}
namespace ConsoleApplication1
{
class Item
{
private String itemName;
private int itemQuantity;
public Item(String itemName, int itemQuantity)
{
this.itemName = itemName;
this.itemQuantity = itemQuantity;
}
public Item()
{
}
public void increaseQuantity(int increaseQuantity)
{
this.itemQuantity += increaseQuantity;
}
public void decreaseQuantity(int increaseQuantity)
{
this.itemQuantity -= increaseQuantity;
}
public String getName()
{
return this.itemName;
}
public void setName(String name)
{
this.itemName = name;
}
public int getQuantity()
{
return this.itemQuantity;
}
public void setQuantity(int x)
{
this.itemQuantity = x;
}
}
}
Your dictionary stores objects of type Item. There's no guarantee that an arbitrary Item will be a MedPack therefore you can't directly call useMedPack on it.
If, in your case, you know that the item is a MedPack you can just cast it:
((MedPack)inventory["MedPack"]).useMedPack();
or in two lines:
MedPack mp = (MedPack)inventory["MedPack"];
mp.useMedPack();
If, at run time, the item is not a MedPack, you'll get an exception.
If you want a single method that can be applied to all item types ,then define it in Item and override it in the sub-classes as necessary:
in Item:
public virtual void UseItem()
{
// base implementtaion
}
in MedPack:
public override void UseItem()
{
// implementation specific to MedPack
}
Related
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);
}
Trying to use a class method, with an instance of that class in a separate method. Used method RandomItem() to build instance of class Item. Using ViewItem() to display the item, it says:
"The name 'item1' does not exist in this context"
This is my code:
class Program
{
public class Item
{
public string part1;
public Item(string _part1)
{
part1 = _part1;
}
public void PrintItem()
{
Console.WriteLine(part1);
}
}
public static void Main()
{
RandomItem();
ViewItem();
}
public static void RandomItem() {
string randomPart1 ="";
Item item1 = new Item(randomPart1);
}
public static void ViewItem() {
item1.PrintItem(); //this is where the error is "The name 'item1' does not exist in the current context
}
}
}
Kind of new to c#, just not sure why I can't access item1.PrintItem(), or if I'm even allowed to do this. Any help would be much appreciated.
If a variable is local, you need to return it so the caller can access it. Then you need to pass it as an argument for another method to access it.
public static void Main()
{
var item = RandomItem(); //Retrieve item
ViewItem(item); //Then pass it in
}
public static Item RandomItem() {
string randomPart1 ="";
Item item1 = new Item(randomPart1);
return item1; //Return the item to Main
}
public static void ViewItem(Item item1) { //Accept item as argument from Main
item1.PrintItem();
}
There are other options-- for example, you could use a static variable-- but this is the most common way to do it.
You need to declare "item1" outsite of your "RandomItem()" method as a member of your "Program" class.
On a console App the entry point is a static method. So everything you declare in your "Program" class must be static.
class Program
{
public static Item item1; // the static field that contains the instance of your "Item"
public class Item
{
public string part1;
public Item(string _part1)
{
part1 = _part1;
}
public void PrintItem()
{
Console.WriteLine(part1);
}
}
public static void Main()
{
RandomItem();
ViewItem();
}
public static void RandomItem() {
string randomPart1 ="";
item1 = new Item(randomPart1);
}
public static void ViewItem() {
item1.PrintItem();
}
}
I have a dictionary of string/interface class. I cannot include the fields I need in the interface child-classes, but each of them have the same public variables I need to change.
I want to loop through the dictionary and change them to values within the looping class. I cannot do so, as the interface does not contain those variables.
How should I go about this?
public class MakeAbility : MonoBehaviour
{
public BlockScriptableObject block;
public IDictionary<string, IAbility> abilities_parts = new Dictionary<string, IAbility>();
public Targeting target_manager;
public AbAttack attack;
public AbCast cast;
public AbDefend defend;
public AbDefendOther defend_other;
public AbPotion potion;
private void Start()
{
abilities_parts.Add("attack", attack);
abilities_parts.Add("cast", cast);
abilities_parts.Add("defend", defend);
abilities_parts.Add("defend_other", defend_other);
abilities_parts.Add("potion", potion);
}
public void trigger_button()
{
foreach (var i in abilities_parts.Values)
{
i.block_attack_damage = block.attack_damage;
i.targeting_for_attack = target_manager;
}
public interface IAbility
{
void Use();
void Enact();
}
public class AbPotion : MonoBehaviour, IAbility
{
public Targeting targeting_for_attack;
public int block_attack_damage = 10;
public void Use()
{
}
public void Enact()
{
}
}
Your properties are NOT properties of IAbility. They are properties of the AbPotion class. You will need an if else statement on the types to set them individually. The thing is, they should have been set before adding to the Dictionary and probably thru the constructor.
public void trigger_button()
{
foreach (var i in abilities_parts.Values)
{
if(i is AbPotion)
{
var potion = i as AbPotion;
potion.block_attack_damage = block.attack_damage;
potion.targeting_for_attack = target_manager;
}
else if(i is AbAttack)
{
var attack = i as AbAttack;
attack.Property1= value1;
attack.Property2 = value2;
}
}
}
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.
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).