5

C#

Hi all,

I pass an object to a method.

I want to cast that object to its specific class so I can perform its own specific methods? How can I do that?

Move( new Cat() );
Move( new Pigeon() );

public void Move(object objectToMove)
{
    if(objectToMove== Cat)
    {
        Cat catObject = objectToMove as Cat;
        catObject.Walk();
    }
    else if(objectToMove== Pigeon)
    {
        Rat pigeonObject = objectToMove as Pigeon;
        pigeonObject.Fly();
    }
}
divinci
  • 22,329
  • 11
  • 45
  • 56
  • 5
    Out of interest - why do you need to do this? It feels to me like there's something up with the design if you've lost the object type and need to "reclaim" it. (e.g. an object received via interface then cast back to it's proper type, you must just need to add a new method the interface to do the "object-specific" work?) Just asking. – Joel Goodwin Nov 03 '09 at 10:34
  • 5
    The most important thing to realize here is that the `Move()` method should be a **member** of your classes, e.g. you should have `Cat.Move()` and `Pigeon.Move()`, each of them calling the appropriate private method. Most of the time, when you use the `is` keyword, you are doing something unnecessarily nasty. – vgru Nov 03 '09 at 10:40
  • I can only agree with Groos comment. This is a perfect example of what inheritance and OOP is for! – SoftMemes Nov 03 '09 at 10:45
  • 2
    Hmm. Pigeon.Fly, Cat.Walk... It doesn't look like production code to me. It could be that the example code given is just to illustrate roughly what he wants to do, and you can't infer potential design improvements from it. Sometimes you just need to down-cast. – mackenir Nov 03 '09 at 12:12
  • Hi all, yes it is just example code for my own knowledge - I will actually be passing an interface into the method, but wanted to know some extra info around the subject :) – divinci Nov 03 '09 at 15:40

7 Answers7

25

The right way to do this would be:

public interface ICanMove
{
   void Move();
}

implemented by both Cat and Pigeon:

public class Cat : ICanMove
{
   public void Move() { /* do something */ }
}

public class Pigeon : ICanMove
{
   public void Move() { /* do something */ }
}

And then call it like this:

public void Move(ICanMove objectThatMoves)
{
   objectThatMoves.Move();
}

You gain several things:

  1. You can only call the Move(ICanMove objectThatMoves) for an object that implements the interface. If you try to pass a different object, your program will not compile.
  2. You lose the conditional statement. If you decide to add a Dog which implements ICanMove to your program, the Move method will work great without any modifications. In your version you will have to change the method.

Imagine how much work you would have if you had is statements all over your program?

vgru
  • 49,838
  • 16
  • 120
  • 201
9

Well, you can use the is keyword like this:

if(objectToMove is Cat)
{
    ...
}
else if(objectToMove is Pigeon)
{
    ...
}
Yogesh
  • 14,498
  • 6
  • 44
  • 69
  • 1
    Don't use `is` and `as` together as it is redundant casting (for more info please read my article about this: http://togaroga.com/blog/2009/09/two-casts-are-not-better-than-one/). A more scalable solution for the OP's particular problem is proposed by Groo here: http://stackoverflow.com/questions/1666496/c-casting-an-object-parameter-into-that-objects-type/1666564#1666564. – Andrew Hare Nov 03 '09 at 14:06
4

One small point... It's more correct to not use as and is together as the second type check implied is redundant. Either use as in the following manner:

Cat catObject = objectToMove as Cat;
if(catObject!=null)
    catObject.Walk();
else 
{
    Pigeon pigeonObject = objectToMove as Pigeon;        
    if(pigeonObject!=null)
        pigeonObject.Fly();    
}

Or do a direct cast as you are sure it will succeed; this is both more efficient and more clear:-

if(objectToMove is Cat)    
{        
     Cat catObject = (Cat)objectToMove;        
     catObject.Walk();    
}    
else if(objectToMove is Pigeon)    
{        
     Pigeon pigeonObject = (Pigeon)objectToMove;        
     pigeonObject.Fly();    
}

Using a direct cast you get an exception if it fails, rather than a null. People reading the code will know without context "We're not just trying to see if it can be treated as something, we're sure that we've ascertained that it should be possible.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • is uses two typechecks. as uses 1. I af prette sure as the more efficient one. All through the .NET framework as is used. – Esben Skov Pedersen Nov 03 '09 at 10:49
  • @EsbenP: Not sure what your point is, but mine is 1. Efficiency:- `y=x as Y; if(y!=null){y.Something();}` is more efficient than if(x is Y) { y=x as Y; y.Something();}` - the `as` is checking whether it really `is` Y *again*. 2. Clarity: if you've already established that x `is` Y, using `as` instead of a direct cast, i.e. `(Y)` is less clear as to what you're doing. Thus, instead of saying `if(x is Y){Y y=x as Y;}` one should instead say `if(x is Y){Y y=(Y)x;}` This makes it clear that you're not trying to do anything conditional. – Ruben Bartelink Nov 03 '09 at 11:29
  • On the other hand, I know many people mis-use `as`. I also know that in concrete terms, the perf, IL size or JITted code size difference between a conditional cast and a non-conditional [Direct] cast is pretty moot. But doing a type check followed by a conditional cast of the same type *is* redundant. – Ruben Bartelink Nov 03 '09 at 11:31
  • I sometimes wish there was a `to` operator that does a direct cast so that I could just say "you should use `to` instead of `as` in this case as you're sure the cast will succeeed". But there isnt (and the intent in the design (IIRC explained in Stroustrups D&E) is for casts to stick out as being ugly so you're motivated to avoid using them). The problem is that in C# you're not choosing between a static_cast, a reinterpret_cast or a C-style cast -- you're choosing between a pretty `as` and an ugly direct cast – Ruben Bartelink Nov 03 '09 at 11:35
  • Related question: http://stackoverflow.com/questions/2139798/why-is-the-c-as-operator-so-popular/2140136#2140136 – Ruben Bartelink Jan 27 '10 at 08:36
4

Another solution instead of using an interface would be to have a base class and to override the Move() method in each derived class.

    abstract class Animal
    {
        public abstract void Move();
    }

    public class Cat : Animal
    {
        public override void Move()
        {
            //Cat specific behavior
        }
    }

    public class Bird : Animal
    {
        public override void Move()
        {
            //Bird specific behavior
        }
    }

I think using a base class is better if you have properties or methods that are common to all your "animals". This way you don't have to duplicate code in each class that implements the "IAnimal" interface described above.

Of course if there are no common elements interface works fine.

FrenchData
  • 630
  • 5
  • 10
3

You can use the "is" operator to check if an object is an implementation of a certain class.

if (objectToMove is Cat)
ynnckcmprnl
  • 4,382
  • 1
  • 24
  • 25
2

Just a bit of change in your and it's good to go.

Move( new Cat() );
Move( new Pigeon() );

public void Move(object objectToMove)
{
    if(objectToMove is Cat)
    {
        Cat catObject = objectToMove as Cat;
        catObject.Walk();
    }
    else if(objectToMove is Pigeon)
    {
        Rat pigeonObject = objectToMove as Pigeon;
        pigeonObject.Fly();
    }
}
this. __curious_geek
  • 42,787
  • 22
  • 113
  • 137
0

What I would do is this:

interface IAnimal
{
    void Move();
}

An interface

Then my cat and my bird:

class Cat : IAnimal
{
    public void Move()
    {
        //a cat moves by walking
        this.Walk();
    }

    public void Walk()
    {
        //do logic
    }
}

class Bird : IAnimal
{
    public void Move()
    {
        this.Fly();
    }

    public void Fly()
    {
        //do fly logic
    }
}

So when you're done you get this:

public void Move(IAnimal animal)
{
    animal.Move();
}

And should you want to check the type of your animal you can do this:

public void PrintType(IAnimal animal)
{   
    Console.WriteLine(typeof(animal));
    //or
    Console.WriteLine(animal.GetType());
}
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Anemoia
  • 7,928
  • 7
  • 46
  • 71