4

WARNING: While the accepted answer is correct, to anyone attempting to implement this, please see @CodesInChaos's comments as well. This was a bad idea on my part.


I have a generic interface and a class that implements the interface 'n' number of times:

interface IA<T>
{
    T Foo();
}

class Baz1 { }
class Baz2 { }

class Bar : IA<Baz1>, IA<Baz2>
{
    Baz1 Foo() { return new Baz1(); }
    Baz2 Foo() { return new Baz2(); }
}

How can I use reflection to call both Foo methods on an instance of Bar?

I already have the following code to get the interface definitions and the generic type parameters:

class Frobber
{
    void Frob(object frobbee)
    {
        var interfaces = frobbee.GetType()
            .GetInterfaces()
            .Where(i => i.IsGenericType &&
                i.GetGenericTypeDefinition() == typeof(IA<>).GetGenericTypeDefinition());
    }
}
Lars Kemmann
  • 5,509
  • 3
  • 35
  • 67
  • In my specific situation, instead of `IA`, I have a `Bar` class that implements `IObservable`, `IObservable`, etc. - and also `IObserver`, `IObserver`, etc. - but I didn't want to cloud the discussion. Maybe that's relevant though? – Lars Kemmann May 02 '15 at 16:44
  • Okay, thinking about this now, that *is* actually relevant, because the `IObservable` use case (calling two different `Subscribe()` methods on `Baz`) is different from the `IObserver` use case (calling two different `Subscribe()` methods with `Baz` as an argument). So let's just consider the former, as in the example code I've provided. – Lars Kemmann May 02 '15 at 16:46
  • 1
    Are you sure you want inheritance instead of composition here? – CodesInChaos May 02 '15 at 16:51
  • Good question! I thought about composition but in this particular case I chose inheritance intentionally. – Lars Kemmann May 02 '15 at 16:53
  • 1
    Then I'll be direct. You shouldn't do that. `IObservable` is covariant. So if you implement it for different `T`s, returning a different sequence, you won't get a correct implementation for `IObservable`. – CodesInChaos May 02 '15 at 16:57
  • I see your point. :) Thank you. – Lars Kemmann May 02 '15 at 16:59
  • 1
    Similar questions about implementing two separate versions of `IEnumerable`: 1) [A Class with multiple `IEnumerable` interfaces on it - What to do with the non-generic method?](http://stackoverflow.com/questions/7851014/a-class-with-multiple-ienumerablet-interfaces-on-it-what-to-do-with-the-non) 2) [Implementing multiple IEnumerables in C#](http://stackoverflow.com/questions/4196621/implementing-multiple-ienumerables-in-c-sharp) – CodesInChaos May 02 '15 at 17:06
  • Indeed. The problem is that I had been thinking about the problem in the wrong terms. – Lars Kemmann May 03 '15 at 01:36

1 Answers1

4

Definitions:

interface IA<T>
{
    T Foo();
}

class Baz1 { }
class Baz2 { }

class Bar : IA<Baz1>, IA<Baz2>
{
    Baz2 IA<Baz2>.Foo()
    {
        return new Baz2(); 
    }

    Baz1 IA<Baz1>.Foo()
    {
        return new Baz1(); 
    }
}

Code:

    Bar b = new Bar();
    var methods = typeof(Bar).GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IA<>)).Select(i => i.GetMethod("Foo"));
    foreach(var method in methods)
    {
        var invoked = method.Invoke(b, null); // or add params instead of null when needed
    }
Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
  • 1
    Thanks, I forgot about having to make the implementations explicit. – Lars Kemmann May 02 '15 at 16:58
  • Note you should only be getting one or zero elements in `methods`, so `Select` might need to be replaced with a more appropriate `FirstOrDefault` + check. – SimpleVar May 02 '15 at 17:13
  • @YoryeNathan you can get more than one `MethodInfo` objects and you will. You can not use FirstOrDefault it beats the puropse. – Stavros Zotalis May 02 '15 at 17:34
  • Multiple implementations of an explicit interface method, by the same class? How – SimpleVar May 02 '15 at 19:54
  • If the method is overloaded, you are correct. But in case the method is known to have only one signature, I'd go with `FirstOrDefault` – SimpleVar May 02 '15 at 20:29