views:

474

answers:

5

Hello Everyone, I have a property that returns an interface. During debugging I can break on what was returned and while it is the interface, Visual Studio is smart enough to know the derived type that it actually is. I assume it's using reflection or something. I'm not sure. My question is, can I have that same info avaialble to me at runtime so I can create a variable of the appropriate type and cast the interface as that? Here is what I am saying...

IPreDisplay preDisplay = cb.PreDisplay;

If preDisplay is a RedPreDisplay I would like to be able to code

RedPreDisplay tmp = preDisplay as RedPreDisplay;

Or if preDisplay were a GreenPreDisplay...

GreenPreDisplay tmp = preDisplay as GreenPreDisplay;

etc... I would like to avoid a messy switch statement if possible, and If I could use generics that would be great.

If you have any advice or examples of how I can do this, please share.

Thanks for the help, ~ck in San Diego

+3  A: 

Depending on what it is you're trying to do, you should probably add an action method/ property to the interface, that way you don't need to know the type - which is polymorphism.

E.g:

 IResultLiteEntity preDisplay = cb.PreDisplay;
 preDisplay.Render (); // New Render method on the interface...
Kieron
+16  A: 

When you hit a situation where you need to do this, it means you're doing something wrong. You need to back up and figure out why your design demands that you do this. If you find yourself stuck there, I strongly recommend you post a new question to get help with the design - there are a lot of smart people here who can help.

To answer your question directly, no - you cannot do this without some kind of if/else or conditional, because you have to be explicit with static types. You could use reflection to call the method, but since you seem to need to call something the interface does not support - but some objects do - you would need to code a per-static-type condition anyway to call that method. Just code the types directly.

Edit: per the discussion in the comments, the best solution to this is to add a second interface to the classes which have this other property or method. Then you can do a simple check:

IPreDisplay display = cb.PreDisplay;
IOtherInterface displayAsOther = display as IOtherInterface;
if(displayAsOther != null)
{
    displayAsOther.OtherMethod();
}
Rex M
It is possible without a switch/else in in C# 4.0 you can use the dynamic keyword to dispatch at runtime a type to a method with a specific signature.
LBushkin
@LBushkin very true, but c# 4.0 is not released yet and it's not reasonable to assume everyone who has problems 4 may solve can simply switch over to the beta. Or even when it is RTM.
Rex M
And even if it's possible to do in C# 4, it's still probably not a good idea. Even in languages that supports it, like VBScript, I never use it.
Guffa
@RexM - I know that C# 4.0 is not yet available, however, it worth having people be aware of such techniques as it will eventually be available.@Guffa - Why do you think that this type of dynamic dispatch is in and of itself a bad idea? Like any technique it should be applied only in appropriate circumstances. Without knowing more about what the OP was trying to do and what constraints were in place, you can't necessarily say whether there are better alternatives.
LBushkin
IPreDisplay is used all over the application. Some objects that Implement it, do not have the Properties I need that the interface doesn't provide. I think my best bet is to create a new Interface and a small set of objects implement it. Perhaps that is the solution here.
Hcabnettek
@Kettenbach that definitely sounds like a good approach. Interfaces should be as limited in purpose-scope as possible, so if you have a new subset of functionality that some objects implement, that should be a separate interface.
Rex M
+4  A: 

The entire purpose of using interfaces is that the executing code does not have to be aware of the exact type. Try to expose all the information you might need via the interface itself, so you have no need to cast.

Understandably, you may still need to cast an interface to a concrete implementation (specific type) on a rare occasion. If you could provide a bit more context, that might be helpful.

Noldorin
+3  A: 

@Rex M is absolutely correct. The problem lies with your code and the underlying structure. As a rule, you should not do what you are trying to do; code against the interface only.

That said, there is the is operator that might help you if you've inherited bad code and need to monkey-patch it. For example:

if(myInstance is MyBaseType)
{
  MyBaseType myInstanceAsBaseType = myInstance as MyBaseType;
  // handle MyBaseType specific issue
}
else if(myInstance is MyOtherBaseType)
{
  MyOtherBaseType myInstanceAsOtherBaseType = myInstance as MyOtherBaseType;
  // handle MyOtherBaseType specific issue.
}

Generics will not help you, nor will you be able to do this as part of a switch statement. But it'll get you something working, albeit working in a very ugly way.

Randolpho
+1  A: 

As other responders have pointed out, you probably should consider why your design requires different logic for different types that can't be pulled up into an interface.

However, assuming that there are good reasons for this, you only have a few options:

  1. Use reflection. This is generally slow and error prone code, that's also fragile when your implementation changes (i.e. methods are renamed, etc).
  2. Use an if/else if/else pattern to dispatch based on runtime checks of the type. This is pretty much your only other choice in pre-4.0 versions of C#.
  3. If you are using C# 4.0, you can use assign the object to a dynamic var, and at runtime dispatch to an overloaded method whose signature varies for each of the types supported (see example below).

Here's a C# 4.0 dynamic dispatch example:

void Foo()
{
  dynamic preDisplay = cb.PreDisplay;
  DoSomethingWith( preDisplay );  // runtime dispatch using dynamic runtime (DLR)
}

void DoSomethingWith( RedPreDisplay r ) { ... }  // code specific to RefPreDisplay
void DoSomethingWith( GreenPreDisplay g ) { ... } // code specific to GreenPreDisplay
void DoSomethingWIth( IPreDisplay o ) { ... }  // catch-all
LBushkin