well ive read a lot of posts here and there about why it isnt reliable to raise events via reflection.. my problem is this.. im using PostSharp to define an interface which allows a class to notify before and after a property is changed..
the NotifyAttribute ive created needs to be able to raise the PropertyBeforeChange and PropertAfterChange events.. thing is, even though i can retrieve the event, its GetRaiseMethod() returns null and hence i cannot raise the said events.. how can i go about doing that?
using PostSharp.Aspects;
namespace Core
{
public delegate void PropertyBeforeChangeEventHandler(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel);
public delegate void PropertyAfterChangeEventHandler(object sender, string sPropertyName, object oOldValue, object oNewValue);
public interface INotify
{
event PropertyBeforeChangeEventHandler PropertBeforeChange;
event PropertyAfterChangeEventHandler PropertyAfterChange;
}
[Serializable]
public sealed class NotifyAttribute : LocationInterceptionAspect, IInstanceScopedAspect
{
bool _NotifyBefore { get; set; }
bool _NotifyAfter { get; set; }
public NotifyAttribute() { _NotifyAfter = true; }
public NotifyAttribute(bool bNotifyBefore, bool bNotifyAfter) { _NotifyBefore = bNotifyBefore; _NotifyAfter = bNotifyAfter; }
public override void OnSetValue(LocationInterceptionArgs args)
{
INotify oNotify = args.Instance as INotify;
if (oNotify == null) return;
object oCurrentValue = args.GetCurrentValue();
object oProposedValue = args.Value;
if (object.Equals(oCurrentValue, oProposedValue)) return;
bool bCancel = false;
if (_NotifyBefore)
{
var oObj = args.Instance.GetType().GetEvent("PropertyBeforeChange");
// RAISE EVENT HERE
}
if (bCancel) return;
args.Value = oProposedValue;
args.ProceedSetValue();
if (_NotifyAfter)
{
var oObj = args.Instance.GetType().GetEvent("PropertyAfterChange");
// RAISE EVENT HERE
}
}
public object CreateInstance(AdviceArgs adviceArgs) { return this.MemberwiseClone(); }
public void RuntimeInitializeInstance() { }
}
}
having defined this interface and this attribute, i can use it as follows..
public class Test : INotify
{
public event PropertyBeforeChangeEventHandler PropertyBeforeChange;
public event PropertyAfterChangeEventHandler PropertyAfterChange;
[Notify]
public string Name { get; set; }
}
Test oTest = new Test();
oTest.PropertyBeforeChange += Test_PropertBeforeChange;
oTest.PropertyAfterChange += Test_PropertyAfterChange;
oTest.Name = "Asim";
void Test_PropertBeforeChange(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel)
{
}
void Test_PropertyAfterChange(object sender, string sPropertyName, object oOldValue, object oNewValue)
{
}
Related
I try to return an object in my event:
public class MyEvent : EventArgs
{
public Channels number = new Channels(); // Channels is a class where i declared only variables( i try to return all variables inside this class)
public MyEvent(Channels numero)
{
return numero;
}
}
This code doesn't work and i don't know how to return an object which contains my variables of Channels.
Change that to:
public class MyEvent : EventArgs
{
public Channels Number {get;}
public MyEvent(Channels numero)
{
Number = numero;
//return numero; You cannot use "return" in a CTOR!
}
}
Then you can use it in an EventHandler like this:
void MyEventHandler( object sender, MyEvent e )
{
// sender => object that raised the event
// e => an instance of `MyEvent`, having a property, we can read.
var channels = e.Number; // use the info
}
Of course you would have registered it, before it will be triggered:
someInstanceProvidingTheEvent.MyEventHappened += MyEventHandler;
Raising the event works something like this:
// assume we are in the class that offers the Event
public event EventHandler<MyEvent> MyEventHappened;
protected virtual void OnMyEventHappened( Channels chans )
{
// You may want to add some error fortification, here
MyEventHappened?.Invoke(this, new MyEvent(chans));
}
// raise it
public void SomeMethod(){
var theChannels = new Channels();
// yadda yadda
// now it happens!
OnMyEventHappened(theChannels);
}
public class MyEvent : EventArgs
{
public Channels _channels { get; set; }
public MyEvent(Channels numero)
{
_channels = numero;
}
}
public class Program
{
public Main()
{
Channels myChannels = new Channels();
MyEvent _myEvent = new MyEvent(myChannels);
var youWant = _myEvent._channels;
}
}
I'm trying to use a RealProxy with Unity3D and I'm getting this error:
NullReferenceException: Object reference not set to an instance of an object
(wrapper ldfld) System.Object.__ldfld_wrapper_000001804BA82020_System.Single(object,intptr,intptr,intptr)
Player.PlayerController.Setup (UMVC.Core.MVC.Interfaces.IBaseView`1[TModel] view) (at Assets/Player/PlayerController.cs:27)
UMVC.Core.MVC.BaseView`2[TModel,TController].Awake () (at <51bf8ffda04c457aa9f14160e1112cbf>:0)
This error is thrown when I try to get a public member of my PlayerModel like playerModelInstance.jumpPower.
The code behind PlayerController.cs:
namespace Player
{
public class PlayerController : BaseController<PlayerModel>
{
public override void Setup(IBaseView<PlayerModel> view)
{
base.Setup(view) // you can see the implementation below
// in theory the `Model` is fully initialized after base.Setup(view)
Debug.Log(Model.jumpPower); // THE CODE IS CRASHING HERE
}
} //end PlayerController
}
The PlayerModel:
using System;
using UMVC.Core.MVC;
using UnityEngine;
namespace Player
{
[Serializable]
public class PlayerModel : BaseModel
{
public float jumpPower = 12f;
public override void Initialize()
{
base.Initialize();
isOnFieldDidUpdateEnabled = false;
isOnFieldWillUpdateEnabled = false;
}
}
}
The ModelProxy: you can view the implementation here: UMVC.Core/MVC/ModelProxy.cs
The BaseModel:
which inherit from MarshalByRefObject in order to make RealProxy working.
The BaseController:
using UMVC.Core.MVC.Interfaces;
namespace UMVC.Core.MVC
{
public abstract class BaseController<TModel> where TModel : BaseModel
{
protected TModel Model { get; set; }
protected IBaseView<TModel> View;
protected ModelProxy<TModel> ModelProxy;
public virtual void Setup(IBaseView<TModel> view)
{
View = view;
ModelProxy<TModel> modelProxy = ModelProxy<TModel>.Bind(View.GetModel());
Model = modelProxy.GetTransparentProxy();
Model.Initialize();
ModelProxy = modelProxy;
SubscribeEvents();
}
public virtual void LateSetup()
{
}
protected virtual void SubscribeEvents()
{
ModelProxy.OnFieldWillUpdate += View.OnFieldWillUpdate;
ModelProxy.OnFieldDidUpdate += View.OnFieldDidUpdate;
}
}
}
namespace UMVC.Core.MVC
{
[Serializable]
// MarshalByRefObject are necessary in order to RealProxy be able to work
public abstract class BaseModel : MarshalByRefObject, IBaseModel
{
public bool isOnFieldWillUpdateEnabled = true;
public bool isOnFieldDidUpdateEnabled = true;
public virtual void Initialize()
{
}
}
}
Note: The ModelProxy is working outside of a Unity Project because Unit Tests cover this code, visible from UMVC.Core/Tests/MVCTests.cs.
EDIT 1:
Changing Debug.Log(Model.jumpPower); to Debug.Log(Model); outputs
Player.PlayerModel
UnityEngine.Debug:Log(Object)
Player.PlayerController:Setup(IBaseView`1) (at Assets/Player/PlayerController.cs:27)
UMVC.Core.MVC.BaseView`2:Awake()
So it seems that Model is correctly instanciated but I can't access to my public members.
As a replacement of RealProxy I have used a pattern like the microsoft INotifyPropertyChanged:
public interface INotifyPropertyChanged
{
event Delegates.OnFieldWillUpdate OnFieldWillUpdate;
event Delegates.OnFieldDidUpdate OnFieldDidUpdate;
}
Modified my BaseModel as follows:
[Serializable]
public abstract class BaseModel: IBaseModel, INotifyPropertyChanged {
public bool isOnFieldWillUpdateEnabled = true;
public bool isOnFieldDidUpdateEnabled = true;
public virtual event Delegates.OnFieldWillUpdate OnFieldWillUpdate;
public virtual event Delegates.OnFieldDidUpdate OnFieldDidUpdate;
public virtual void Initialize() {}
protected virtual void RaiseOnFieldWillUpdate<T>(T newValue, [CallerMemberName] string propertyName = null) {
var eventArgs = new PropertyChangedEventArgs(propertyName);
object oldValue;
var type = GetType();
var field = type.GetField(eventArgs.PropertyName);
if (field != null) {
oldValue = field.GetValue(this);
}
else {
oldValue = type.GetProperty(eventArgs.PropertyName) ? .GetValue(this);
}
OnFieldWillUpdate?.Invoke(this, newValue, oldValue, eventArgs);
}
protected virtual void RaiseOnFieldDidUpdate<T>(T newValue, [CallerMemberName] string propertyName = null) {
OnFieldDidUpdate?.Invoke(this, newValue, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool Set<T>(ref T field, T value, Expression<Func<T>> property) {
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
string propertyName = ((MemberExpression) property.Body).Member.Name;
if (isOnFieldWillUpdateEnabled) RaiseOnFieldWillUpdate(value, propertyName);
field = value;
if (isOnFieldDidUpdateEnabled) RaiseOnFieldDidUpdate(field, propertyName);
return true;
}
}
Use it like that:
[Serializable]
public class MvcModel: BaseModel
{
[SerializeField] private int val;
public int Value {
get => val;
set => Set(ref val, value, () => Value);
}
}
I'm trying to create an event handler and I am getting the error : 'Object' does not contain a definition for ListCountLastH " and same for listCountLH.
Changing private List to public List makes no difference and I am pretty sure I have to initialise the lists in protected override void OnStateChange() but anyway public /*override*/ void OnStateChange() makes no difference, so any help would be greatly appreciated.
public class Incrementer : DivergenceList5m throws ListCountLastH and listCountLH inaccessible due to protection level
DivergenceList5m.ListCountLastH.Add(DivergenceList5m.listCountLH); throws additionally "An object reference is required for the non-static field, method or property'Namwspace.Class.ListCountLastH " and same for listCountLH and I just don't know what else to try...
Thank you
public class Divergence5min : Strategy
{
public int listCountLH;
private List<int> ListCountLastH;
private List<double> LastHSwDMI;
protected override void OnStateChange()
{
ListCountH = new List<int>();
LastHSwDMI = new List<double>();
}
...
protected override void OnBarUpdate()
{
if (!LHsDmiAdd && b > 1 && HSwDMI[b-1] - HSwDMI[b] > 0.001 && HSwDMI[b-1] - HSwDMI[b-2] > 0.001)
{
LastHSwDMI.Add(HSwDMI[b-1]);
listCountLH = LastHSwDMI.Count;
ListCountLastH.Add(listCountLH);
...
}
}
public class Incrementer
{
public EventHandler ItemAdded;
public void AddItem()
{
base.ListCountLastH.Add(base.listCountLH);
if (ItemAdded != null)
ItemAdded(this, null);
}
}
}
Modifying like this with the private List still throws the 'inaccessible due to its protection level' error. The subscriber class does not throw any errors, though
public class Incrementer : DivergenceList5m
{
public event EventHandler ItemAdded;
public void AddItem()
{
var divList = new DivergenceList5m();
divList.ListCountLastH.Add(divList.listCountLH);
if (ItemAdded != null)
ItemAdded(this, null);
}
}
class AddingItems
{
public int ItemsCount {get; private set;}
public void Items (Incrementer incrementer)
{
ItemsCount = 0;
incrementer.ItemAdded += AddItems;
}
void AddItems (object source, EventArgs e)
{
ItemsCount++;
}
}
Last code version
public class Divergence5min : Strategy
{
public int listCountLH;
public List<int> ListCountLastH;
public List<double> LastHSwDMI;
...
public class Incrementer : DivergenceList5m
{
public event EventHandler ItemAdded;
public void AddItem()
{
var divList = new DivergenceList5m();
divList.ListCountLastH.Add(divList.listCountLH);
if (ItemAdded != null)
ItemAdded(this, null);
}
}
class AddingItems
{
public int ItemsCount {get; private set;}
public void Items (Incrementer inc)
{
ItemsCount = 0;
inc.ItemAdded += AddItems;
}
void AddItems (object source, EventArgs e)
{
ItemsCount++;
}
}
Looks like you are calling base.listCountLH in a class which inherits from object (i.e. you have not inherited it from anything - object is the default type). You need to inherit from Divergence5min like this:
public class Incrementer: Divergence5min
In order to now call ListCountLastH from here, that property should be at least protected in order to access it from an inherited class:
protected List<int> ListCountLastH;
To access non-static members on a class you need to instantiate it first with:
var divList = new DivergenceList5m();
divList.ListCountLastH.Add(divList.listCountLH);
public interface IConnector
{
void Connect();
event EventHandler<EventArgs> Received;
// and more
}
public class ConnectorA: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
public class ConnectorB: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
Now let's say I have a factory like this:
public interface IConnectorFactory
{
void Create(string type);
}
public class ConnectorFactory: IConnectorFactory
{
public void Create(string type)
{
switch(type)
{
case "A":
return new ConnectorA();
case "B":
return new ConnectorB();
}
}
}
I inject this factory via constructor to Manager class:
public class Manager
{
IConnectorFactory _factory;
IConnector _actualConnector;
string _type;
public string Type
{
get
{
return _type;
}
set
{
_type = value;
if (_actualConnector != null)
{
_actualConnector.Disconnect();
_actualConnector.Received -= ReceivedFunc;
}
_actualConnector = _factory.Create(Type);
_actualConnector.Received += ReceivedFunc;
}
}
public Manager(IConnectorFactory factory)
{
_factory = factory;
}
public void Connect()
{
_actualConnector.Connect();
}
public void DoSomethingElse()
{
_actualConnector.DoSomethingElse();
_actualConnector.Received += ReceivedFunc;
}
public void ReceivedFunc()
{
}
}
Type is property that is set external (for example binded to UI ComboBox). Problem is that every time it is changed I have to unsubscribe old event and subscribe to new event. Isn't there some pattern for subscribing to interface event?
When you assign a value to _actualConnector:
_actualConnector = _factory.Create(Type);
your're just changing a field and the old object has still its Received event handled by ReceivedFunc. This has nothing to do with interfaces, this is the way objects and references work in .NET
I try to run some code when collection is changed. I keep collection as property in Data class:
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set { deviceAdjustedConfigurations = value; }
}
and register it in code like that:
Data.DeviceAdjustedConfigurations.CollectionChanged += new NotifyCollectionChangedEventHandler(DeviceAdjustedConfigurationsCollectionChanged);
But after registration CollectionChanged is null and the appropriate code in delegated method is not run. In this place DeviceAdjustedConiguration already contains some data. What am I doing wrong?
You should avoid having a set property accessor for collection types, one reason being the one you experienced here with events. Another problem is if someone caches the collection and adds items to it later.
var old = obj.DeviceAdjustedConfigurations;
obj.DeviceAdjustedConfigurations = new ObservableCollection<OfferedConfiguration>();
old.Add(new OfferedConfiguration()); // what should happen here?
instead, remove the set-accessor and use the existing collection directly.
obj.DeviceAdjustedConfigurations.Add(new OfferedConfiguration());
If you really need to set the collection, you need to handle this with for instance a property change event from the class that owns the DeviceAdjustedConfigurations.
public class Item
{
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set
{
if (deviceAdjustedConfigurations != value)
{
onDeviceConfigurationsChanging(deviceAdjustedConfigurations, value);
deviceAdjustedConfigurations = value;
}
}
}
public static event EventHandler<ConfigurationChangedEventArgs> DeviceConfigurationsChanging;
private static void onDeviceConfigurationsChanging(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
var handler = DeviceConfigurationsChanging;
if (handler != null)
{
handler(null, new ConfigurationChangedEventArgs(oldList, newList));
}
}
}
public class ConfigurationChangedEventArgs : EventArgs
{
public ConfigurationChangedEventArgs(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
OldList = oldList;
NewList = newList;
}
public ObservableCollection<OfferedConfiguration> OldList { get; private set; }
public ObservableCollection<OfferedConfiguration> NewList { get; private set; }
}
public class Consumer
{
public void foo()
{
Item.DeviceConfigurationsChanging += updateEvents;
}
private void updateEvents(object sender, ConfigurationChangedEventArgs args)
{
args.OldList.CollectionChanged -= onCollectionChanged;
args.NewList.CollectionChanged += onCollectionChanged;
}
private void onCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { }
}