Bauss
Bauss

Reputation: 2797

Explicit Interface And Generic / Dynamic Type Conversion

Before I state my issue let me give some background information on what I'm trying to do, because perhaps there's a better way to achieve it.

I got a class C which inherits two interfaces A and B.

public interface A
{
    void DoStuff();
}

public interface B
{
    void DoStuff();
}

C implements two different bodies for DoStuff.

void A.DoStuff()
{
    Console.WriteLine("A");
}

void B.DoStuff()
{
    Console.WriteLine("B");
}

I know of course DoStuff can be called like below.

var c = new C();
var a = (A)c;
a.DoStuff();
var b = (B)c;
b.DoStuff();

What I want to do though is to avoid converting C to either A or B and then calling DoStuff on A or B.

So what I though was that I could implement a generic method to do this for me and so I first tried like this.

public void DoStuff<T>()
{
    var i = (T)this;
    i.DoStuff();
}

Of course that won't work considering you can convert this to T and it causes a compiler error, so my next step was to try to create a static method that passed the instance of this and then did the conversion in there.

private static To ConvertToDoStuffInterface<From, To>(From from)
{
    return (To)from;
}

I did not expect that to work, but it was worth a try. As I expected it would cause a compiler error, because From cannot explicitly convert to To.

My last resort was using the dynamic keyword and of course it smoothy gets through ConvertToDoStuffInterface, but once I try to call DoStuff it throws a RuntimeBinderException which I kinda suspected it might would. I know why the exception is thrown and it's of course because it can't properly bind the result from ConvertToDoStuffInterface to any type and some kind of casting is required I assume.

Here is the code for the dynamic usage.

private static dynamic ConvertToDoStuffInterface<T>(dynamic from)
{
    return (T)from;
}

public void DoStuff<T>()
{
    var i = ConvertToDoStuffInterface<T>(this);
    i.DoStuff();
}

My question though is if there is no better way to achieve this using either generics or dynamic, how would I go about doing the conversion from dynamic to T? Is there a way that I could check if the passed type to DoStuff<T> has a method DoStuff and then allow what I attempted at first?

To clarify what I want to be able to do is something like this

var c = new C();
c.DoStuff<A>(); // Calls A.DoStuff()
c.DoStuff<B>(); // Calls B.DoStuff()

Here is my C implementation.

public class C : A, B
{        
    public void DoStuff<T>()
    {
        // ... DoStuff<T> implementation here ...
    }

    void A.DoStuff()
    {
        Console.WriteLine("A");
    }

    void B.DoStuff()
    {
        Console.WriteLine("B");
    }
}

Upvotes: 2

Views: 152

Answers (3)

D Stanley
D Stanley

Reputation: 152521

I agree that casting is probably the cleanest (assuming you can't fix the name conflict in the interfaces), but another way would be to create two instance methods on C to wrap the interface calls:

public class C : A, B
{        
    public void DoStuff<T>()
    {
        // ... DoStuff<T> implementation here ...
    }

    void A.DoStuff()
    {
        Console.WriteLine("A");
    }

    void B.DoStuff()
    {
        Console.WriteLine("B");
    }

    public void DoStuffAsA() { ((A)this).DoStuff(); }
    public void DoStuffAsB() { ((B)this).DoStuff(); }
}

At least it gives you compile-time safety that dynamic, reflection and generics don't.

Upvotes: 1

alek kowalczyk
alek kowalczyk

Reputation: 4936

To have the behavior exactly as you want, you can use reflection:

class C : A, B
{
    void B.DoStuff()
    {
        Console.WriteLine("B");
    }

    void A.DoStuff()
    {
        Console.WriteLine("A");
    }

    public void DoStuff<T>() 
    {
        var mi = typeof(T).GetMethod("DoStuff");
        mi.Invoke(this, new object[] { });
    }
}

then the call:

        var c = new C();
        c.DoStuff<A>();
        c.DoStuff<B>();
        Console.ReadLine();

works as expected. Obviously you would have to make proper validation in the DoStuff method, if the "DoStuff" method exists and so on.

However - I don't know the use case - but it seems for me not so obvious why this is for you bether than casting.

Upvotes: 1

dcastro
dcastro

Reputation: 68640

You could use these two InvokeAsextension methods

public static void InvokeAs<T>(this object obj, Action<T> action)
{
    action((T) obj);
}

public static TResult InvokeAs<T, TResult>(this object obj, Func<T, TResult> func)
{
    return func((T) obj);
}

And then use like this:

var c = new C();

c.InvokeAs<A>(x => x.DoStuff());
c.InvokeAs<B>(x => x.DoStuff());

But as pointed out in the comments, ((B) c).DoStuff() is not all that bad.

Upvotes: 2

Related Questions