views:

107

answers:

5

I have an object o which guaranteed at runtime to be one of three types A, B, or C, all of which implement a common interface I. I can control I, but not A, B, or C. (Thus I could use an empty marker interface, or somehow take advantage of the similarities in the types by using the interface, but I can't add new methods or change existing ones in the types.)

I also have a series of methods MethodA, MethodB, and MethodC. The runtime type of o is looked up and is then used as a parameter to these methods.

public void MethodA(A a) { ... }
public void MethodB(B b) { ... }
public void MethodC(C c) { ... }

Using this strategy, right now a check has to be performed on the type of o to determine which method should be invoked. Instead, I would like to simply have three overloaded methods:

public void Method(A a) { ... } // these are all overloads of each other
public void Method(B b) { ... }
public void Method(C c) { ... }

Now I'm letting C# do the dispatch instead of doing it manually myself. Can this be done? The naive straightforward approach doesn't work, of course:

Cannot resolve method 'Method(object)'. Candidates are:

  • void Method(A)
  • void Method(B)
  • void Method(C)
A: 

If the 3 class A, B and C implements the interface I you should do nothing. It's the runtime that choose the right method for you:

A a = new A();
class.Method(a); // will calll Method(A a)

B b = new B();
class.Method(b); // will call Method(B b)
ema
This doesn't answer the question correctly (or at least misunderstands what I was saying). I don't have an `A` or `B`; I have an `object` (see the first sentence).
Evan Barkley
A: 

Preseumably O is declared as type/interface I then instantiated as either a b or c. The way to do it would be to have a single public method the took a param of type I and then do your logic within that method e.g. call private method A B or C.

Reading your edited post, o is of type object, remember to be clear when you take about an object becuase as well as being a c# type it is a generic oop term for an instance of a class.

Why are you declaring it as an object, rather than an i, might it be something else later?

If so to use your overloaded methods you would have to unbox it before calling the method. e.g.

if (o.GetType() == typeof(a))
{
   oa = (a)o;
   Method(oa);
}
else if(o.GetType() == typeof(b))
{
    ...
}

Etc.

Alternatively if you made the method take a paramater of type i you could do something like:

i oi = (i)o;
Method(oi);

But you would still need to do something like the the first example within your public method.

Ben Robinson
+5  A: 

How about something like this?

private Dictionary<Type, Action<I>> _mapping = new Dictionary<Type, Action<I>>
{ 
  { typeof(A), i => MethodA(i as A)},
  { typeof(B), i => MethodB(i as B)},
  { typeof(C), i => MethodC(i as C)},
};

private void ExecuteBasedOnType(object value)
{
    if(_mapping.ContainsKey(value.GetType()))
       _mapping(value.GetType())(value as I);
}
Brian Genisio
Ha, this is actually what I currently have right now! Obviously it's not as good as letting C# do the dispatch somehow, though.
Evan Barkley
If you have control over the I, A, B and C, you could add something to I called ExecuteMethod() and just call it on I. If not, you have to map it somehow.
Brian Genisio
This is your best option if you can't apply Kobi's suggestion. A double dispatch / visitor pattern can't be done unless you control the classes either.
Rob Fonseca-Ensor
I think it's looking like this is the best route to go, so I'm accepting this answer.
Evan Barkley
If you take this suggestion and make `_mapping` static and change `ExecuteBasedOnType` to an extension method of `I` then you get the nice syntax as in Kobi's suggestion without refactoring `A`, `B` and `C`
Cornelius
@Cornelius: That's clever! I'll do that.
Evan Barkley
This is basically what the "dynamic" feature in C# 4 does. (Except of course that it builds the lambdas and the lookup table as needed instead of knowing them beforehand.)
Eric Lippert
It seems there's a specialized class for that: `KeyedByTypeCollection`: http://msdn.microsoft.com/en-us/library/ms404549.aspx . The documentation is confusing, but it may be for just such cases (not sure though, I just found it, and it reminded me of this answer).
Kobi
+7  A: 

If you can refactor this, move the method to the interface and have each class has its implementation:

I i = o as I;
i.Method();
Kobi
+1 Exactly what I was about to say.
Jason Punyon
I edited to clarify that I control `I`, but not `A`, `B`, or `C`. But this would be great if I could!
Evan Barkley
@Evan, use partial classes (see my answer)
tster
@Evan - how do you get `o`? If you control the function that return it, you can inherit from A B and C, and have the new classes implement a new interface. I suppose you don't control that either though, so you won't get the new types...
Kobi
@tster If you make a class partial all its partial declarations must be made in the same assembly and in all declarations of the class must be marked partial, so you still need control of `A`, `B` and `C` and if you have control of it you just as well refactor the code as in Kobi's suggestion.
Cornelius
@Cornelius, the very fact that A, B and C all inherit from I which he does control, makes me think that he has a least a little bit of control over the classes themselves. I guess, that's what makes this question so strange.
tster
This is really the right answer when I can control the implementing types. I wish I could mark two answers accepted.
Evan Barkley
@tster: Yes, it's a very strange situation. A, B, and C are generated partial classes that I can't touch (by fiat), but they're partial. Applying an interface that simply takes advantage of similarities is not considered "touching" it, so I can get away with this.
Evan Barkley
@Evan, This is THE situation that partial classes exist for. Add an interface like I recommend in my answer, and then implement that interface for each class in a separate file from the one generated!
tster
@​​​​​​​​​​​​​​tster - partial classes won't help here, you can't use them unless you have the source of A B and C - it is just a way to organize code in several files, and has no effect after compilation. Try it out. The documentation says "All of the parts must be available at compile time to form the final type ... All partial-type definitions meant to be parts of the same type must be defined in the same assembly" - http://msdn.microsoft.com/en-us/library/wa80x488%28VS.80%29.aspx
Kobi
A: 

I'm not 100% sure if this will work in your scenario, but it sounds like you might be able to use partial classes:

public interface IMethodCallable
{
    public void Method();
}

public partial class A : IMethodCallable
{
    public void Method()
    {
        ....
    }
}

then for the usage:

object o = getObject(); // we know this is A, B or C
((IMethodCallable)o).Method();
tster