Reputation: 2797
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
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
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
Reputation: 68640
You could use these two InvokeAs
extension 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