This is the solution I have used:
First off, many thanks to @HimBromBeere for providing the basis of this solution. In essence, what he has posted is the answer, however included here are the pertinent details of how I implemented this architecture.
Given the following interfaces:
interface IContract
{
void CommonMethod();
}
interface IContract<T> : IContract where T : EventArgs
{
event EventHandler<T> CommonEvent;
}
I have a classes, which for all intents and purposes are declared as follows:
abstract class BaseControl
{
protected void SubscribeToContract<Type>(IContract<Type> contract)
where Type : EventArgs
{
contract.ContractChanged +=
delegate(object sender, Type e)
{
// Calls this method to perform other actions:
OnContractChanged(e);
};
}
// Called when IContract<>.ContractChanged is raised:
protected virtual void OnContractChanged(EventArgs e);
}
class SpecialControl : BaseControl, IContract<SpecialEventArgs>
{
...
protected override OnContractChanged(EventArgs e)
{
base.OnContractChanged();
PerformAction(e);
}
...
void PerformAction(EventArgs e)
{
// We can also now find out if "e" is SpecialEventArgs
if (e is SpecialEventArgs)
{
...
}
...
}
...
}
Where the method SubscribeToContract<Type>()
is a protected member inherited from BaseControl
. This allows derived classes to implement their own methods like PerformAction()
while abstracting away having to worry about dealing with the generic interface stuff (goal).
Within my class there is an event
which I use the following statement to determine if the argument implements IContract
, which is the base interface for IContract<>
:
if (e.Control is IContract)
{
// Gets all the IContract<> interfaces e.Control implements:
var generics = GetIContractGenerics(e.Control.GetType());
// There might be multiple IContract<> implementations
// so therefore we need to check them all, even though in
// practice there will likely only be a single instance:
foreach (var generic in generics)
{
var method = this.GetType().GetMethod(
"SubscribeToContract", (BindingFlags.Instance | BindingFlags.NonPublic));
method = method.MakeGenericMethod(generic);
var result = method.Invoke(this, new[] { e.Control });
}
}
In my case setting (BindingFlags.Instance | BindingFlags.NonPublic)
was required in order to get the GetMethod()
function to return the SubscribeToContract<Type>()
method.
Where the method GetIContractGenerics()
was created using the method described in this StackOverflow post:
protected static IEnumerable<Type> GetIContractGenerics(Type type)
{
return
from implementation in type.GetInterfaces()
where implementation.IsGenericType
let definition = implementation.GetGenericTypeDefinition()
where definition == typeof(IContract<>)
select implementation.GetGenericArguments().First();
}
This GetIContractGenerics()
static method is stored in BaseControl
for convenience.
Thats it, everything works! Thanks everyone for your input!