3

Suppose I have an interface defined as follows:

interface IContract
{
    void CommonMethod();
}

and then another interface which inherits from this interface defined in the manner of:

interface IContract<T> : IContract where T : EventArgs
{
    event EventHandler<T> CommonEvent;
}


My specific question is, given any instance implementing IContract, how could I determine if is also IContract<T>, and if so, what the generic type of IContract<T> is without hard-coding each known type of IContract<T> I might encounter.


Ultimately, I would use this determination in order to make a call of the following pattern:

void PerformAction<T>(IContract<T> contract) where T : EventArgs
{
    ...
}
Lemonseed
  • 1,644
  • 1
  • 15
  • 29

2 Answers2

3

As you need an instance of IContract<T> you have to use reflection to get the generic type-param first and than call the appropriate method:

// results in typeof(EventArgs) or any class deriving from them
Type type = myContract.GetType().GetGenericArguments()[0]; 

Now get the generic type-definiton for IContract<T> and get the appropriate method.

// assuming that MyType is the type holding PerformAction
Type t = typeof(MyType).MakeGenericType(type); 
var m = t.GetMethod("PerformAction");

Alternativly if only the method PerforAction is generic instead of MyType:

// assuming that MyType is the type holding PerformAction
Type t = typeof(MyType); 
var m = t.GetMethod("PerformAction").MakeGenericMethod(type);

Now you should be able to invoke the method on an instannce of IContract:

var result = m.Invoke(myInstance, new[] { myContract } );

Where myInstance is of type MyType.

Lemonseed
  • 1,644
  • 1
  • 15
  • 29
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • Nice, I didn't think of that! Unfortunately, I would not be able to utilize this as the class which implements the interface is also a Windows control with design-time support. The generics class declaration (*i.e.*, `class Bar`) is incompatible with the Visual Studio Designer. – Lemonseed Feb 10 '16 at 08:09
  • Use of reflection in your subsequent edit looks promising. I am trying to integrate into a test solution right now. – Lemonseed Feb 10 '16 at 08:15
  • Was able to get solution to work using methods described in this answer. Please see below answer for details/actual implementation. Thanks! – Lemonseed Feb 10 '16 at 19:09
0

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!

Community
  • 1
  • 1
Lemonseed
  • 1,644
  • 1
  • 15
  • 29