6

I have the code:

        foreach(var o in objects)
        {
            o.Update(time);

            if(o is Portal)
            {
                var a = (Portal)o;
                a.Interact(ref player, player.Interact);
            }
            else if(o is Enemy)
            {
                var e = (Enemy)o;
                e.Update(time, player);
            }
        }

I don't know if anything like this is possible?

I want to do it in one line. This is what I have in mind:

(Enemy)o => Update(time, player);

I know it's stupid but I want something similar. The method that has player as a parameter is unique to the Enemy object. I have to parse to call it.

Roman
  • 11,966
  • 10
  • 38
  • 47
Emily
  • 143
  • 10

6 Answers6

8

You can make your loop simpler (in case you use C# 6 or higher):

foreach(var o in objects)
{
    o.Update(time);

    (o as Portal)?.Interact(ref player, player.Interact);
    (o as Enemy)?.Update(time, player);           
}

For C# 5 or lower you should use:

foreach(var o in objects)
{
    o.Update(time);

    if (o is Portal)
    {
        ((Portal)o).Interact(ref player, player.Interact);
    }
    else if(o is Enemy)
    {
        ((Enemy)o).Update(time, player);
    }
}

In this case you have less lines of code but you cast two times.

You can cast only one time:

foreach(var o in objects)
{
    o.Update(time);

    var e = o is Portal;
    if (e != null)
    {
        e.Interact(ref player, player.Interact);
    }
    else
    {
        ((Enemy)o).Update(time, player);
    }
}
Roman
  • 11,966
  • 10
  • 38
  • 47
  • 1
    You should use as casts also in the legacy version. No need for is and then a cast. – Sefe Jan 12 '17 at 10:04
  • `as` is still preferred over `is` and a cast, but then they wouldn't be one-liners anymore (or ugly ones) Good answer, though – Dennis_E Jan 12 '17 at 10:06
  • @Sefe, I edited my answer for the legacy version, did you mean that? – Roman Jan 12 '17 at 10:13
  • No, I meant to drop the is. If you run code analysis on either version it will warn you for the duplicate cast. You should as cast and then check for null. It's not what the op actually wants, but it's best practice. – Sefe Jan 12 '17 at 10:20
4

Try this:

((Enemy)o).Update(time, player);

Remember about possible Null Reference Exception if you didn't check a type of this object. In your code everything is fine.

Pawel Maga
  • 5,428
  • 3
  • 38
  • 62
4

You can replace those two lines with a single line as follows:

else if(o is Enemy)
{
    ((Enemy)o).Update(time, player);
}
Chris Pickford
  • 8,642
  • 5
  • 42
  • 73
3

You call the function in that way:

var a = ((Portal)o).Interact(ref player, player.Interact);

so the type of a will be the returned type of Interact, but in context of your code it won't matter much.

Jodn
  • 314
  • 1
  • 11
  • We can suppose that Interact is a method (doesn't return anything). `var a = ` is useless and will not compile. – Orace Jan 12 '17 at 09:56
1

To go further than other response, you could use the visitor pattern.

First create a IVisitorClient and a IVisitor interface.

interface IVisitorClient
{
    Accept(IVisitor visitor);
}

interface IVisitor
{
    Visit(SceneObject o); // here the types of your objects
    Visit(Enemy e);
    Visit(Portal p);
}

Make your different object implement IVisitorClient.

abstract class SceneObject : IVisitorClient
{
    public virtual void Accept(IVisitor visitor)
    {
         visitor.Visit(this);
    }
}

class Portal : SceneObject
{
...

    public override void Accept(IVisitor visitor)
    {
         visitor.Visit(this);
    }
...
}

class Enemy: SceneObject
{
...

    public override void Accept(IVisitor visitor)
    {
         visitor.Visit(this);
    }
...
}

Then build an updater that implement IVisitor :

class UpdaterVisitor : IVisitor
{
    readonly Player player;
    readonly Time time;

    public UpdaterVisitor(Player player, Time time)
    {
        this.player = player;
        this.time = time;
    }

    public void Visit(SceneObject o)
    {
        e.Update(time);
    }

    public void Visit(Enemy e)
    {
        e.Update(time, player);
    }

    public void Visit(Portal p)
    {
        p.Interact(ref player, player.Interact);
    }
}

Finally, to update the object, the code will look like this.

var updateVisitor = new UpdaterVisitor(player, time);
foreach(var o in objects)
{
    o.Accept(updateVisitor);
}
Orace
  • 7,822
  • 30
  • 45
0

Or do it this way to avoid the double cast...

foreach(var o in objects)
{
    o.Update(time);

    Portal p = o as Portal;

    if(p != null)
    {
        p.Interact(ref player, player.Interact);
    }
    else
    {
        Enemy e = o as Enemy;
        if (e != null)
        {
            e.Update(time, player);
        }
    }
}
Justin Harvey
  • 14,446
  • 2
  • 27
  • 30