Although my problem is more of C# / JSON.NET related I will give you the context.
I am building a game in which I need to implement a Challenge System. For those who might be unfamiliar, Challenges are basically clusters of achievements/tasks that you accomplish to earn rewards.
So I need to have Challenges that have list of tasks that are tracked in game.
e.g. Challenge XYZ has 3 task
1. Kill 3 Enemies
2. Collect 2 Stars
3. Reach Score of 100
So first of all I have a GameState Class. I am using GameState object to store all the in game data.
public class GameState
{
public static string SaveKey = "GameState";
public int Blips { get; set; }
public int ChainClearedCount { get; set;}
public int DotClearedCount { get; set; }
public int RetryUsedCount { get; set; }
public int ContinueUsedCount { get; set; }
public int LevelAttemptedCount { get; set; }
public int LevelClearedCount { get; set; }
public int LevelPlayedCount { get; set; }
public int LevelFailCount { get; set; }
public Dictionary<ChainCombos, int> ComboCountBook = new Dictionary<ChainCombos, int>();
public Dictionary<ChainType, int> ChainTypeCountBook = new Dictionary<ChainType, int>();
public Dictionary<LevelType, int> LevelTypeCountBook = new Dictionary<LevelType, int>();
public Dictionary<GameModes, int> LevelModeCountBook = new Dictionary<GameModes, int>();
public Dictionary<GameModes, int> HighScoreBook = new Dictionary<GameModes, int>();
public Dictionary<GameModes, int> HighLevelBook = new Dictionary<GameModes, int>();
public Dictionary<LevelType, int> PlaygroundLevelCountBook = new Dictionary<LevelType, int>();
public Dictionary<GameDifficulty, int> PlaygroundDifficultyCountBook = new Dictionary<GameDifficulty, int>();
//Followed By Constructors , Accessor Methods and Event Subscribers for GameCore Module
}
So the Game State object's respective fields are updated by the GameCore class (not shown) whenever any event occurs.
I am using JSON.NET to serialize this GameState object so that it can be used to load the game data on game launch.
Now I have my Task Class:
The key part of Task Class is Func Value;
This stores a delegate that is used to query the GameState object.
public class Task
{
protected bool Incremental = true;
Func<int> Value;
public event Action<float> OnProgress;
protected int targetValue;
protected int currentValue;
public bool Completed = false;
public float Progress;
[JsonConstructor]
public Task(int targetValue, Func<int> Value, bool Incremental, bool ConnectedByDefault) //Constructor
{
this.targetValue = targetValue;
this.Value = Value;
this.Incremental = Incremental;
if (ConnectedByDefault)
{
ConnectToGame();
}
}
public void ConnectToGame()
{
GameController.GameStateEvent += UpdateProgress;
CalculateProgress();
}
public void DisconnectFromGame()
{
GameController.GameStateEvent -= UpdateProgress;
}
protected virtual void OnComplete() { Completed = true; }
protected void UpdateProgress(GameState s)
{
if (Value() > currentValue)
{
CalculateProgress();
if (OnProgress != null)
{
OnProgress(Progress);
}
}
}
protected void CalculateProgress()
{
currentValue = Value();
if (currentValue < targetValue)
{
if (Incremental)
{
Progress = ((float)currentValue / (float)targetValue);
}
else
{
Progress = 0f;
}
}
else
{
Progress = 100f;
OnComplete();
}
}
}
Now if i have to declare a Task i do something like this
Task a = new Task(200, () => GameController.CurrentGameState.DotClearedCount, true, false);
As you can see what I am doing is sending a delegate that will be allocated to Value field of the task and will be used internally to query the Game-state object's indicated field (in this case DotClearedCount).
My issue is that I have to serialize the task objects since I need to keep track of player's task progress over multiple game sessions.
JSON.NET is incapable of serializing delegate fields of objects as far as I know. I mean I checked storing a delegate and the JSON output was something like "delegatEntry = null".
So I am unable to wrap my head around the issue of how I am going to save my Task objects such that when they are deserialized and constructed by JSON.NET they get linked back to my GameState object.
I looked in LINQ queries I am still unable to find anything concrete that will help me. I mean as far as I understood I can build LINQ queries on objects like Lists and Dictionaries but then again my GameState objects have a lot of Dictionaries that I want to target separately for different tasks.
Related
I am trying to create a custom dictionary with some methods. I created a struct containing the information for lanes in my game. One information tells me if there is an enemy in the lane(Occupied) and the other if we completed that lane so no more enemies will come there(Completed).
I am able to get the initial information out, but cannot update them with my methods. I construct it by adding all 7 lanes, where none of them are either occupied or completed. Then throughout my game, I would like to mark them either as completed or occupied or free, but even after a lot of time searching around, I couldn't figure out the proper way to call for an update of these items inside my laneInfo property.
public struct laneInfo
{
public bool Occupied;
public bool Completed;
}
public class laneInfoClass : Dictionary<int, laneInfo>
{
public laneInfo laneinfo;
public laneInfoClass()
{
for(int i = 0; i <= 6; i++)
{
this.Add(i, false, false);
}
}
public void Add(int key, bool occupied, bool completed)
{
laneinfo.Occupied = occupied;
laneinfo.Completed = completed;
this.Add(key, laneinfo);
}
public void Complete()
{
laneinfo.Completed = true;
}
public void Occupy()
{
laneinfo.Occupied = true;
}
public void Free()
{
laneinfo.Occupied = false;
}
}
Thanks!
Its fairly rare that your class would inherit from a dictionary/list/collection (why?), more often than not what you are actually modelling is a class which has an instance member which is that same dictionary/list/collection.
In addition, you need some way to notify your class which particular lane you're trying to update, you use an integer key so work with that:
public struct LaneInfo
{
public bool Occupied {get;set;}
public bool Completed {get;set;}
}
public class LaneInfoContainer
{
private Dictionary<int, LaneInfo> laneInfoDict = new Dictionary<int, LaneInfo>();
public LaneInfoContainer()
{
for(int i = 0; i <= 6; i++)
{
this.Add(i, false, false);
}
}
public void Add(int key, bool occupied, bool completed)
{
var laneInfo = new LaneInfo();
laneinfo.Occupied = occupied;
laneinfo.Completed = completed;
this.laneInfoDict.Add(key, laneinfo);
}
public void Complete(int key)
{
laneInfoDict[key].Completed = true;
}
public void Occupy(int key)
{
laneInfoDict[key].Occupied = true;
}
public void Free(int key)
{
laneInfoDict[key].Occupied = false;
}
}
I suspect you might also need some way to read the info about your lanes too, add methods such as
public bool IsComplete(int key)
{
return laneInfoDict[key].Complete;
}
The answer to your question is that you should not use a Dictionary. Iterating a List<T>.Contains is faster than Dictionary<TKey, TValye> lookup for 7 items, especially if you are accessing them by index integer 0-6.
Part from that, Dictionary<TKey, TValue> is already generic and so there is no need to inherit from it. Sometimes you would wrap it, for various reasons (one might be locking). But if it is just for a few methods you can simply add extension methods to the Dictionary<int, LaneInfo>.
public static class LaneExtensionMethods
{
public static bool IsComplete(this Dictionary<int, LaneInfo> dictionary, int key)
{
return dictionary[key].Complete;
}
}
// Use
var d = new Dictionary<int, LaneInfo>();
var isComplete = d.IsComplete(1);
I often replace the int with a type to avoid bugs and confusion in code. This would in your case also allow for specialized extension methods. Casting has zero CPU cost (its just cosmetics in code).
public enum LaneId : Int32 { }
public static class LaneExtensionMethods
{
public static bool IsComplete(this Dictionary<LaneId, LaneInfo> dictionary, LaneId key)
{
return dictionary[key].Complete;
}
}
// Use
var d = new Dictionary<LaneId, LaneInfo>();
var laneId = (LaneId)1; // We cast from integer to LaneId, but use LaneId type everywhere in our app
var isComplete = d.IsComplete(laneId);
I have two classes one called warehouse and one called Warehouselocations.
The wareHouse is currently able to create,store and find boxes in warehouselocation.
But now i also need the warehouse to be able to create a cloned version of wareHouseLocation with all the stored information.
locations = new List<WareHouseLocation>();
This is the list where i store all the information. I want to be able to copy it.
I tried to find the answer my self and even tried some code but so far i had got nothing that works properly.
public WareHouseLocation DeepCopy()
{
foreach (WareHouseLocation wareHouseLocation in locations)
{
if(wareHouseLocation == null)
{
return null;
}
else
{
//Need code here
}
}
return null;
}
The code is currently in the wareHouse class.
I be happy for anything that could help me.
public class WareHouseLocation
{
public int FloorID { get; set; }
public List<I3DStorageObject> storage = new List<I3DStorageObject>();
public double MaxVolume;
public double MaxWeight;
public WareHouseLocation(double height, double width, double depth)
{
MaxVolume = height * width * depth;
MaxWeight = 1000;
}
public bool hasAvailableVolumeForObject(I3DStorageObject s)
{
double currentVolume = 0;
foreach (I3DStorageObject obj in storage)
{
currentVolume += obj.Volume;
}
double available = MaxVolume - currentVolume;
if (s.Volume <= available)
{
return true;
}
else
{
return false;
}
}
}
Here is the code for the WareHouseLocation.
You can achieve it by implementing ICloneable interface:
public class WareHouseLocation : ICloneable
{
public int FloorID { get; set; }
public List<I3DStorageObject> storage = new List<I3DStorageObject>();
public double MaxVolume;
public double MaxWeight;
//rest of code
public object Clone()
{
var copy = (WareHouseLocation)MemberwiseClone();
copy.storage = storage.Select(item => (I3DStorageObject)item.Clone()).ToList();
return copy;
}
}
Since you have a List reference type inside WareHouseLocation, you'll need to properly clone this as well by implementing ICloneable for I3DStorageObject as well, because MemberwiseClone copy the reference only, not the referred object itself
public class I3DStorageObject : ICloneable
{
public double Volume { get; set; }
public object Clone()
{
return MemberwiseClone();
}
}
You can also have a look at MemberwiseClone for details and examples of deep/shallow copy of objects
I think you can use JsonConvert.SerializeObject and JsonConvert.DeserializeObject for copy,
var json = JsonConvert.SerializeObject(put_your_object_here);
var copy = JsonConvert.DeserializeObject<your_object_type>(json);
Setup:
public class Data
{
public int A { get; set; }
public int B { get; set; }
}
public class Runner
{
public static void Run(Data data)
{
data.A = data.B;
data.A = 1;
}
}
class Program
{
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
Runner.Run(data);
}
}
Problem: I need to implement change tracking here for property names not values. Inside Runner.Run on the first line data.A = data.B I need to record somehow that "A" was set to "B" (literally property names) and then on the next line data.A = 1 I need to record that "A" was set to constant and say forget about it.
Constrains:
When setting one property to another (e.g. A = B) that needs to be recorded
When setting property to anything else (e.g. A = 1 or A = B * 2) this change needs to be forgotten (e.g. remember A only)
Suppose this is the tracker contract being used:
void RecordChange(string setterName, string getterName);
void UnTrackChange(string setterName);
Question:
I would like to somehow proxy the Data class so it still can be used in the interface code (e.g. Runner - is a whole bunch of a business logic code that uses Data) INCLUDING strong-typing and it can track it's changes without modifying the code (e.g. there is lots of places like 'data.A = data.B').
Is there any way to do it without resorting to I guess some magic involving IL generation?
Already investigated/tried:
PostSharp interceptors/Castle.DynamicProxy with interceptors - these alone cannot help. The most I can get out of it is to have a value of data.B inside setter interceptor but not nameof(data.B).
Compiler services - haven't found anything suitable here - getting the name of caller doesn't really help.
Runtine code generation - smth like proxy inherited from DynamicObject or using Relfection.Emit (TypeBuilder probably) - I lose typings.
Current solution:
Use the Tracker implementation of the abovementioned contract and pass it around into every function down the road. Then instead of writing data.A = data.B use method tracker.SetFrom(x => x.A, x => x.B) - tracker holds a Data instance and so this works. BUT in a real codebase it is easy to miss something and it just makes it way less readable.
It is the closest the solution I've come up with. It isn't perfect as I still need to modify all the contracts/methods in the client code to use a new data model but at least all the logic stays the same.
So I'm open for other answers.
Here's the renewed Data model:
public readonly struct NamedProperty<TValue>
{
public NamedProperty(string name, TValue value)
{
Name = name;
Value = value;
}
public string Name { get; }
public TValue Value { get; }
public static implicit operator TValue (NamedProperty<TValue> obj)
=> obj.Value;
public static implicit operator NamedProperty<TValue>(TValue value)
=> new NamedProperty<TValue>(null, value);
}
public interface ISelfTracker<T>
where T : class, ISelfTracker<T>
{
Tracker<T> Tracker { get; set; }
}
public class NamedData : ISelfTracker<NamedData>
{
public virtual NamedProperty<int> A { get; set; }
public virtual NamedProperty<int> B { get; set; }
public Tracker<NamedData> Tracker { get; set; }
}
Basically I've copy-pasted the original Data model but changed all its properties to be aware of their names.
Then the tracker itself:
public class Tracker<T>
where T : class, ISelfTracker<T>
{
public T Instance { get; }
public T Proxy { get; }
public Tracker(T instance)
{
Instance = instance;
Proxy = new ProxyGenerator().CreateClassProxyWithTarget<T>(Instance, new TrackingNamedProxyInterceptor<T>(this));
Proxy.Tracker = this;
}
public void RecordChange(string setterName, string getterName)
{
}
public void UnTrackChange(string setterName)
{
}
}
The interceptor for Castle.DynamicProxy:
public class TrackingNamedProxyInterceptor<T> : IInterceptor
where T : class, ISelfTracker<T>
{
private const string SetterPrefix = "set_";
private const string GetterPrefix = "get_";
private readonly Tracker<T> _tracker;
public TrackingNamedProxyInterceptor(Tracker<T> proxy)
{
_tracker = proxy;
}
public void Intercept(IInvocation invocation)
{
if (IsSetMethod(invocation.Method))
{
string propertyName = GetPropertyName(invocation.Method);
dynamic value = invocation.Arguments[0];
var propertyType = value.GetType();
if (IsOfGenericType(propertyType, typeof(NamedProperty<>)))
{
if (value.Name == null)
{
_tracker.UnTrackChange(propertyName);
}
else
{
_tracker.RecordChange(propertyName, value.Name);
}
var args = new[] { propertyName, value.Value };
invocation.Arguments[0] = Activator.CreateInstance(propertyType, args);
}
}
invocation.Proceed();
}
private string GetPropertyName(MethodInfo method)
=> method.Name.Replace(SetterPrefix, string.Empty).Replace(GetterPrefix, string.Empty);
private bool IsSetMethod(MethodInfo method)
=> method.IsSpecialName && method.Name.StartsWith(SetterPrefix);
private bool IsOfGenericType(Type type, Type openGenericType)
=> type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType;
}
And the modified entry point:
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
NamedData namedData = Map(data);
var proxy = new Tracker<NamedData>(namedData).Proxy;
Runner.Run(proxy);
Console.ReadLine();
}
The Map() function actually maps Data to NamedData filling in property names.
I have a few classes which have some primitive fields and I would like to create a generalized wrapper for them in order to access their fields. This wrapper should somehow contain a reference to the fields of my classes so that I can read/write the values of these fields. The idea is to create a genralized architecture for these classes so that I dont have to write code for each of them. The classes have fields which have a number in them which will be used as an Id to access the fields.
This is some example code that might shed some light on my requirement. What I want in the end is to change the value of some field in the object of Fancy1 class without accessing the object itself but through its wrapper.
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
class Fancy2
{
public double level4;
public bool isEnable4;
public double level6;
public bool isEnable6;
public double level7;
}
class FieldWrapper
{
public int id { get; set; }
public object level { get; set; }
public object isEnabled { get; set; }
public FieldWrapper(int id, object level, object isEnabled)
{
this.id = id;
this.level = level;
this.isEnabled = isEnabled;
}
}
class FancyWrapper
{
private Fancy scn;
public FancyWrapper(Fancy scn)
{
if (!(scn is Fancy))
throw new ArgumentException(scn.GetType().FullName + " is not a supported type!");
this.scn = scn;
}
private Dictionary<int, FieldWrapper> fieldLut = new Dictionary<int, FieldWrapper>();
private List<FieldWrapper> _fields { get { return fieldLut.Values.ToList(); } }
public List<FieldWrapper> fields
{
get
{
if (_fields.Count == 0)
{
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(double))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut.Add(satId, new FieldWrapper(satId, fieldInfo.GetValue(scn), true));
}
}
foreach (System.Reflection.FieldInfo fieldInfo in scn.GetType().GetFields())
{
if (fieldInfo.FieldType == typeof(bool))
{
int satId = getIdNr(fieldInfo.Name);
fieldLut[satId].isEnabled = fieldInfo.GetValue(scn);
}
}
}
return _fields;
}
}
private int getIdNr(string name)
{
System.Text.RegularExpressions.Match m = System.Text.RegularExpressions.Regex.Match(name, #"\d+");
return Int32.Parse(m.Value);
}
}
class Program
{
static void Main(string[] args)
{
Fancy1 fancy = new Fancy1();
fancy.level1 = 1;
fancy.isEnable1 = true;
fancy.level2 = 2;
fancy.isEnable2 = false;
fancy.level3 = 3;
FancyWrapper wrapper = new FancyWrapper(fancy);
wrapper.fields[2].level = 10;
// fancy.level2 should somehow get the value I set via the wrapper
Console.WriteLine(fancy.level2);
Console.ReadLine();
}
}
EDIT: Fancy classes cannot be changed since they are part of an interface!
Depending on how many Fancy classes you are dealing with, you could create an adapter/facade class for each the expose a common interface. eg:
class Fancy1
{
public double level1;
public bool isEnable1;
public double level2;
public bool isEnable2;
public double level3;
}
public class FieldWrapper
{
private Action<double> _levelSetter;
private Func<double> _levelGetter;
private Action<bool> _enableSetter;
private Func<bool> _enableGetter;
public double level { get { return _levelGetter(); } set { _levelSetter(value); }}
public bool isEnabled { get { return _enableGetter(); } set { _enableSetter(value); }}
internal FieldWrapper(Func<double> levelGetter, Action<double> levelSetter, Func<bool> enableGetter, Action<bool> enableSetter)
{
_levelGetter = levelGetter;
_levelSetter = levelSetter;
_enableGetter = enableGetter;
_enableSetter = enableSetter;
}
}
abstract class FancyWrapper
{
public FieldWrapper[] Fields { get; protected set; }
}
class Fancy1Wrapper : FancyWrapper
{
private Fancy1 _fancy1;
public Fancy1Wrapper(Fancy1 fancy1)
{
_fancy1 = fancy1;
this.Fields = new[] { new FieldWrapper(() => fancy1.level1, level => _fancy1.level1 = level, () => _fancy1.isEnable1, enable => _fancy1.isEnable1 = enable),
new FieldWrapper(() => fancy1.level2, level => _fancy1.level2 = level, () => _fancy1.isEnable2, enable => _fancy1.isEnable2 = enable), };
}
}
Or you could invest 5 minutes learning data structures. Consider following example:
var levels = new Dictionary<int, bool>
{
{1, true},
{2, false}
};
if (levels[1])
{
//will run, because level 1 is true
}
if (levels[2])
{
//will not run, because level 2 is false
}
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because dictionary does not contain entry for key 3
}
levels.Add(3, false);
if (levels.ContainsKey(3) && levels[3])
{
//will not run, because level 3 is false
}
levels[3] = true;
if (levels.ContainsKey(3) && levels[3])
{
//will run, because level 3 is true
}
That may seem like what you want, but it really isn't. It is extremely awkward on any number of levels. More specifically, pointers are generally rather "Un-C#-like" and having to know about these numbers defeats the point of having separate classes to begin with.
Think closely about what you want to accomplish. If you're having problems translating it into code, we're here to help. :)
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).