##Review
Review
##Proposed Solution
Proposed Solution
##Review
##Proposed Solution
Review
Proposed Solution
###contravariance
If you add the generic constraint where T : class
you can simplify Action<T>
and Action<object>
because one is contravariant with the other.
Action<T> proxy = (T v) => value.Invoke(v);
Action<T> proxy = value;
##Proposed Solution
Store all listeners in a list LinkedList
(allow for duplicates).
private List<object>LinkedList<object> listeners = new List<object>LinkedList<object>();
private void Register(object listener)
{
if (listener == null) return;
listeners.AddAddFirst(listener);
}
private void Unregister(object listener)
{
if (listener == null) return;
listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
Invoke event listeners using the dynamic
keyword. The runtime knows which overload of InvokeChange
to call. I have added one overload to take any Object
as a fallback to avoid runtime exceptions, but this should be unreachable code. The combination of Reverse()
, AddFirst()
and Remove()
ensures correct behavior. Performance is another thing though ..
public void InvokeChange(T value)
{
foreach (var listener in listeners.Reverse())
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
// -> see comments that Action<T> and Action<object> can be combined!
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}
###contravariance
If you add the generic constraint where T : class
you can simplify Action<T>
and Action<object>
because one is contravariant with the other.
Action<T> proxy = (T v) => value.Invoke(v);
Action<T> proxy = value;
##Proposed Solution
Store all listeners in a list (allow for duplicates).
private List<object> listeners = new List<object>();
private void Register(object listener)
{
if (listener == null) return;
listeners.Add(listener);
}
private void Unregister(object listener)
{
if (listener == null) return;
listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
Invoke event listeners using the dynamic
keyword. The runtime knows which overload of InvokeChange
to call. I have added one overload to take any Object
as a fallback to avoid runtime exceptions, but this should be unreachable code.
public void InvokeChange(T value)
{
foreach (var listener in listeners)
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
// -> see comments that Action<T> and Action<object> can be combined!
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}
##Proposed Solution
Store all listeners in a LinkedList
(allow for duplicates).
private LinkedList<object> listeners = new LinkedList<object>();
private void Register(object listener)
{
if (listener == null) return;
listeners.AddFirst(listener);
}
private void Unregister(object listener)
{
if (listener == null) return;
listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
Invoke event listeners using the dynamic
keyword. The runtime knows which overload of InvokeChange
to call. I have added one overload to take any Object
as a fallback to avoid runtime exceptions, but this should be unreachable code. The combination of Reverse()
, AddFirst()
and Remove()
ensures correct behavior. Performance is another thing though ..
public void InvokeChange(T value)
{
foreach (var listener in listeners.Reverse())
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
// -> see comments that Action<T> and Action<object> can be combined!
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}
- 14.1k
- 3
- 40
- 101
Store all listeners in a setlist (allow for duplicates).
private HashSet<object>List<object> listeners = new HashSet<object>List<object>();
private boolvoid Register(object listener)
{
if (listener == null) return false;return;
return listeners.Add(listener);
}
private boolvoid Unregister(object listener)
{
if (listener == null) return false;return;
return listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
public void InvokeChange(T value)
{
foreach (var listener in listeners)
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
// -> see comments that Action<T> and Action<object> can be combined!
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}
Store all listeners in a set.
private HashSet<object> listeners = new HashSet<object>();
private bool Register(object listener)
{
if (listener == null) return false;
return listeners.Add(listener);
}
private bool Unregister(object listener)
{
if (listener == null) return false;
return listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
public void InvokeChange(T value)
{
foreach (var listener in listeners)
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}
Store all listeners in a list (allow for duplicates).
private List<object> listeners = new List<object>();
private void Register(object listener)
{
if (listener == null) return;
listeners.Add(listener);
}
private void Unregister(object listener)
{
if (listener == null) return;
listeners.Remove(listener);
}
public event Action<T> ChangeValue
{
add => Register(value);
remove => Unregister(value);
}
public event Action<object> ChangeObject
{
add => Register(value);
remove => Unregister(value);
}
public event Action ChangeEmpty
{
add => Register(value);
remove => Unregister(value);
}
public void InvokeChange(T value)
{
foreach (var listener in listeners)
{
Invoke((dynamic)listener, value);
}
}
// these methods require equivalent method signatures ->
// -> see comments that Action<T> and Action<object> can be combined!
private void Invoke(Action<T> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action<object> action, T value)
{
action.Invoke(value);
}
private void Invoke(Action action, T value)
{
action.Invoke();
}
private void Invoke(Object unsupportedEventListenerType, T value)
{
// ignore or throw exception ..
}