I've started working with in-process because out of process is such pain in the ass and noticed that I need to read some things many times and somewhere (names for example) it looks like waste of resources. So I've made simple caching system and here it is:
This interface is required for my invalidator. It's useful when you want to invalidate other classes besides CachedProperty.
Code:
internal interface IInvalidatable {
void Invalidate();
bool IsValid { get; }
}
Here is class that takes care of invalidating items each frame. It uses WeakReference because we don't want to prevent GC collecting those objects. It's calling "Invalidate" method on each frame - you will need to setup own endscene hook or something.
Code:
internal class Invalidator {
private Invalidator() {
items = new LinkedList<WeakReference>();
Direct3D.RegisterEndSceneCallback( new EndSceneCallback( direct3D_EndScene ), CallbackPriority.Critical );
}
private LinkedList<WeakReference> items;
private void direct3D_EndScene( IntPtr instance ) {
LinkedList<WeakReference> toRemove = new LinkedList<WeakReference>();
foreach ( WeakReference item in items ) {
if ( item.IsAlive )
( ( IInvalidatable )item.Target ).Invalidate();
else
toRemove.AddLast( item );
}
foreach ( WeakReference item in toRemove )
items.Remove( item );
}
public void Register( IInvalidatable item ) {
items.AddLast( new WeakReference( item ) );
}
#region Static members
public static readonly Invalidator Instance = new Invalidator();
#endregion
}
And finally the main class. It will register itself automatically in Invalidator. Here you must supply method that takes care of updating and optionally how many frames should value live. 1 means value can be refreshed once every frame, 10 means value can be refreshed once in 10 frames. Value is refreshed only on demand.
Code:
public class CachedProperty<T> : IInvalidatable {
public CachedProperty( Func<T> update, int framesToLive = 1 ) {
this.update = update;
this.framesToLive = framesToLive;
age = framesToLive + 1;
Invalidator.Instance.Register( this );
}
private Func<T> update;
private int framesToLive;
private T value;
private int age;
private void Update() {
value = update();
age = 1;
}
#region IInvalidatable
void IInvalidatable.Invalidate() {
age++;
}
public bool IsValid {
get {
return age < framesToLive;
}
}
#endregion
#region Operators
public static implicit operator T( CachedProperty<T> self ) {
if ( !self.IsValid )
self.Update();
return self.value;
}
#endregion
}
Example:
Code:
public class WarcraftObject {
internal WarcraftObject( IntPtr address ) {
this.address = address;
name = new CachedProperty<string>( new Func<string>( () => {
return Process.Read<string>( Delegates.Get<CGObject_C__GetNameDelegate>( address, 52 )( address ) );
} ), 10 );
}
protected IntPtr address;
private CachedProperty<string> name;
#region Properties
public string Name {
get { return name; }
}
#endregion
}
Any criticism is welcome